7.1 TCP IP的11种状态

  在研究TCP IP的11种状态之前,我们先看一下服务器中存在僵尸进程的情况。

服务器是多进程模型,客户端是单进程。

服务器程序如下:

  1 #include <sys/types.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <stdio.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <arpa/inet.h>
  8 #include <sys/socket.h>
  9 #include <netinet/in.h>
 10 #include <sys/socket.h>
 11 #include <netinet/ip.h> /* superset of previous */
 12
 13
 14 int main()
 15 {
 16     int sockfd = 0;
 17     sockfd = socket(AF_INET, SOCK_STREAM, 0);
 18
 19     if(sockfd == -1)
 20     {
 21         perror("socket error");
 22         exit(0);
 23     }
 24
 25     struct sockaddr_in addr;
 26     addr.sin_family = AF_INET;
 27     addr.sin_port = htons(8001);
 28     inet_aton("192.168.31.128", &addr.sin_addr);
 29     //addr.sin_addr.s_addr = inet_addr("192.168.6.249");
 30     //addr.sin_addr.s_addr = INADDR_ANY;
 31
 32     int optval = 1;
 33     if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
 34     {
 35         perror("setsockopt error");
 36         exit(0);
 37     }
 38
 39     if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
 40     {
 41         perror("bind error");
 42         exit(0);
 43     }
 44
 45     if(listen(sockfd, SOMAXCONN) < 0)
 46     {
 47         perror("listen error");
 48         exit(0);
 49     }
 50
 51     struct sockaddr_in peeraddr;
 52     socklen_t peerlen;
 53
 54     int conn = 0;
 55
 56     while(1)
 57     {
 58         conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
 59         if(conn == -1)
 60         {
 61             perror("accept error");
 62             exit(0);
 63         }
 64
 65         char *p = NULL;
 66         int peerport = 0;
 67         p = inet_ntoa(peeraddr.sin_addr);
 68         peerport = ntohs(peeraddr.sin_port);
 69         printf("peeraddr = %s\n peerport = %d\n", p, peerport);
 70
 71         pid_t pid = fork();
 72
 73         if(pid == -1)
 74         {
 75             perror("fork error");
 76             exit(0);
 77         }
 78
 79         if(pid == 0)
 80         {
 81             char recvbuf[1024] = {0};
 82             int ret = 0;
 83             while(1)
 84             {
 85                 ret = read(conn, recvbuf, sizeof(recvbuf));
 86
 87                 if(ret == 0)
 88                 {
 89                     printf("peer closed \n");
 90                     exit(0);
 91                 }
 92                 else if(ret < 0)
 93                 {
 94                     perror("read error");
 95                     exit(0);
 96                 }
 97
 98                 fputs(recvbuf, stdout);
 99
100                 write(conn, recvbuf, ret);
101             }
102         }
103     }
104
105     close(conn);
106     close(sockfd);
107
108     return 0;
109 }

客户端程序如下:

 1 #include <sys/types.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <string.h>
 6 #include <errno.h>
 7 #include <arpa/inet.h>
 8 #include <sys/socket.h>
 9 #include <netinet/in.h>
10 #include <sys/socket.h>
11 #include <netinet/ip.h> /* superset of previous */
12
13 int main()
14 {
15     int sockfd = 0;
16     sockfd = socket(AF_INET, SOCK_STREAM, 0);
17
18     struct sockaddr_in addr;
19     addr.sin_family = AF_INET;
20     addr.sin_port = htons(8001);
21     inet_aton("192.168.31.128", &addr.sin_addr);
22     //addr.sin_addr.s_addr = inet_addr("192.168.31.128");
23
24     if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 )
25     {
26         perror("connect error");
27         exit(0);
28     }
29
30     char recvbuf[1024] = {0};
31     char sendbuf[1024] = {0};
32     int ret = 0;
33
34     while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
35     {
36         write(sockfd, sendbuf, strlen(sendbuf));
37
38         ret = read(sockfd, recvbuf, sizeof(recvbuf));
39
40         fputs(recvbuf, stdout);
41         memset(recvbuf, 0, sizeof(recvbuf));
42         memset(sendbuf, 0, sizeof(sendbuf));
43
44     }
45
46     close(sockfd);
47
48     return 0;
49 }

执行结果如下:

可以看到,当客户端使用ctrl+c关闭时,服务器中的子进程成了僵尸进程。这是因为,子进程死了,但是没有进程给它收尸,我们可以调用signal(SIGCHLD, SIG_IGN)函数告诉内核,忽略SIGCHLD信号,让内核来处理SIGCHLD信号,也即让内核收尸,但是工程中一般不这样做。一般是注册信号让父进程调用wait来收尸。

修改服务器程序,加入信号处理函数,如下所示:

  1 #include <sys/types.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <stdio.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <signal.h>
  8 #include <arpa/inet.h>
  9 #include <sys/socket.h>
 10 #include <netinet/in.h>
 11 #include <sys/socket.h>
 12 #include <netinet/ip.h> /* superset of previous */
 13
 14 void handler(int num)
 15 {
 16     printf("child die\n");
 17     wait(NULL);
 18 }
 19
 20 int main()
 21 {
 22     int sockfd = 0;
 23     signal(SIGCHLD, handler);
 24     sockfd = socket(AF_INET, SOCK_STREAM, 0);
 25
 26     if(sockfd == -1)
 27     {
 28         perror("socket error");
 29         exit(0);
 30     }
 31
 32     struct sockaddr_in addr;
 33     addr.sin_family = AF_INET;
 34     addr.sin_port = htons(8001);
 35     inet_aton("192.168.31.128", &addr.sin_addr);
 36     //addr.sin_addr.s_addr = inet_addr("192.168.6.249");
 37     //addr.sin_addr.s_addr = INADDR_ANY;
 38
 39     int optval = 1;
 40     if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
 41     {
 42         perror("setsockopt error");
 43         exit(0);
 44     }
 45
 46     if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
 47     {
 48         perror("bind error");
 49         exit(0);
 50     }
 51
 52     if(listen(sockfd, SOMAXCONN) < 0)
 53     {
 54         perror("listen error");
 55         exit(0);
 56     }
 57
 58     struct sockaddr_in peeraddr;
 59     socklen_t peerlen;
 60
 61     int conn = 0;
 62
 63     while(1)
 64     {
 65         conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
 66         if(conn == -1)
 67         {
 68             perror("accept error");
 69             exit(0);
 70         }
 71
 72         char *p = NULL;
 73         int peerport = 0;
 74         p = inet_ntoa(peeraddr.sin_addr);
 75         peerport = ntohs(peeraddr.sin_port);
 76         printf("peeraddr = %s\n peerport = %d\n", p, peerport);
 77
 78         pid_t pid = fork();
 79
 80         if(pid == -1)
 81         {
 82             perror("fork error");
 83             exit(0);
 84         }
 85
 86         if(pid == 0)
 87         {
 88             char recvbuf[1024] = {0};
 89             int ret = 0;
 90             while(1)
 91             {
 92                 ret = read(conn, recvbuf, sizeof(recvbuf));
 93
 94                 if(ret == 0)
 95                 {
 96                     printf("peer closed \n");
 97                     exit(0);
 98                 }
 99                 else if(ret < 0)
100                 {
101                     perror("read error");
102                     exit(0);
103                 }
104
105                 fputs(recvbuf, stdout);
106
107                 write(conn, recvbuf, ret);
108             }
109         }
110     }
111
112     close(conn);
113     close(sockfd);
114
115     return 0;
116 }

执行结果如下:

由上图可以看到服务器中子进程死掉后,不存在僵尸进程。而且服务器还正常工作,我们重启客户端,依然可以正常发送数据。

原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9403359.html

时间: 2024-10-10 21:55:44

7.1 TCP IP的11种状态的相关文章

【Linux网络基础】TCP/IP协议簇的详细介绍(三次握手四次断开,11种状态)

一.TCP/IP协议簇(DoD参考模型) 用于简化OSI层次,以及相关的标准. 传输控制协议(tcp/ip)簇是相关国防部DoD所创建的,主要用来确保数据的完整性以及在毁灭性战争中维持通信 是由一组不同功能的协议组合在一起构成的协议簇 利用一组协议完成OSI所实现的功能 1. TCP/IP 协议簇中的相关协议 TCP/IP协议簇--应用层: TCP/IP协议簇--主机到主机层: TCP与UDP对比: TCP相关报文结构: 源端口:即本地发起连接的端口 目标端口:即要访问的服务的端口 序列号:因为

TCP协议11种状态集!

TCP协议的11种状态集 ### tcp协议11种状态集转换"三次握手5种状态,四次挥手6种状态"服务端:closed-listen-syn_rcvd-established-close_wait-last_ack-close客户端:closed-syn_send-established-fin_wait1-fin_wait2-time_wait-close1. tcp三次握手状态集转换:服务端:(1)closed-listen(开启相应服务),只有在listen状态服务端才可能建立请

TCP/IP是一种十一状态

2.全部11种状态 2.1.客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT . 2.2.服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK . 2.3.共有的:(1)CLOSED (2)ESTABLISHED . TCP状态迁移 大家对netstat -a命令很熟悉,但是,你有没有注意到STATE一栏呢,基本上显示着established,time_

TCP之11种状态变迁

1. TCP 之11种状态变迁 TCP 为一个连接定义了 11 种状态,并且 TCP 规则规定如何基于当前状态及在该状态下所接收的分节从一个状态转换到另一个状态.如,当某个应用进程在 CLOSED 状态下执行主动打开时,TCP 将发送一个 SYN,且新的状态是 SYN_SENT.如果这个 TCP 接着接收到一个带 ACK 的 SYN,它将发送一个 ACK,且新的状态是 ESTABLISHED.这个最终状态是绝大多数数据传送发送的状态. 自 ESTABLISHED 状态引出的两个箭头处理连接的终止

python TCP协议详解 三次握手四次挥手和11种状态

11种状态解析 LISTEN  --------------------  等待从任何远端TCP 和端口的连接请求. SYN_SENT  ---------------  发送完一个连接请求后等待一个匹配的连接请求. SYN_RECEIVED  --------  发送连接请求并且接收到匹配的连接请求以后等待连接请求确认. ESTABLISHED  -----------  表示一个打开的连接,接收到的数据可以被投递给用户.连接的数据传输阶段的正常状态. FIN_WAIT_1  --------

TCP三次握手和四次挥手以及11种状态

TCP三次握手和四次挥手以及11种状态 1.三次握手 置位概念:根据TCP的包头字段,存在3个重要的标识ACK.SYN.FIN ACK:表示验证字段 SYN:位数置1,表示建立TCP连接 FIN:位数置1,表示断开TCP连接 三次握手过程说明: 1.由客户端发送建立TCP连接的请求报文,其中报文中包含seq序列号,是由发送端随机生成的,并且将报文中的SYN字段置为1,表示需要建立TCP连接.(SYN=1,seq=x,x为随机生成数值)2.由服务端回复客户端发送的TCP连接请求报文,其中包含seq

TCP113次握手--4次挥手及---11种状态

第1章 tcp三次握手 过程 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认:SYN:同步序列编号(SynchronizeSequence Numbers). 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态: 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,

[svc]tcp三次握手四次挥手&amp;tcp的11种状态(半连接)&amp;tcp的time-wait

TCP的状态转化过程(11中状态)以及TIME_WAIT状态 高性能网络 | 你所不知道的TIME_WAIT和CLOSE_WAIT 我相信很多都遇到过这个问题.一旦有用户在喊:网络变慢了.第一件事情就是,netstat -a | grep TIME_WAIT | wc -l 一下.哎呀妈呀,几千个TIME_WAIT. 为什么TIME_WAIT这么多 TCP连接的"三次握手"与"四次挥手" TCP漏洞:半连接 TCP半连接与SYN攻击 TCP连接状态详解 原文地址:h

TCP/IP中的TIME_WAIT状态

毫无疑问,TCP中有关网络编程最不容易理解的是它的TIME_WAIT状态,TIME_WAIT状态存在于主动关闭socket连接的一方. TIME_WAIT状态存在的理由: TCP/IP协议就是这样设计的,是不可避免的.主要有两个原因: 1)可靠地实现TCP全双工连接的终止 TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK