socket编程(7)

目标:

  • TCP 11种状态
  • 连接建立三次握手、连接终止四次握手
  • TIME_WAIT与SO_REUSEADDR
  • SIGPIPE

一、TCP11中状态

 

当客户端和服务器端同时关闭的时候才会产生CLOSING状态。

 


我们通过程序来看一下这11中状态。

LISTEN
启动服务器,观察状态
netstat -an |grep tcp |grep 5188(端口)
发现这个套接字(这个端口)处于监听状态

ESTABLISH
接下来我们发起一个连接,启动客户端
我们发现有两个 ESTABLISH状态,第一个为客户端的,第二个为服务器端的。因为我们是
在同一台机器上做实验的,实际上客户端应该不会显示服务器端的状态。

由于SYN_SEND 和 SYN_RCVD 状态的持续时间太短   我们观察不到。

现在,我们假定服务器端主动关闭:
那么服务器端就会依次进入 FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT状态

 

我们先找到服务器进程

现在我们杀死服务器端  kill -9 3221

然后我们查看一下状态 发现服务器端处于FIN_WAIT_2状态。为什么没有处于TIME_WAIT
状态呢???原因在于这边read没有机会返回0.。为甚吗没有机会返回0??我们要看客户
端程序才知道问题。

 

 

 

我们假定客户端先关闭:如何让客户端关闭呢? 让fgets()返回为NULL. 输入ctr+d

 

如果服务器处于TIME_WATI状态,那么要保留两倍的MLS时间,才会据需处于监听状态。会
导致服务器端无法立即重新启动,如果我们没有调用SO_REUSEADDR的话。这就是我们之前
我们说的服务器端应该设置SO_REUSEADDR地址重复利用。

 

二、SIGPIPE信号产生的原因


刚刚我们发现,服务器端关闭之后,客户端仍然可以向服务器发送数据。这是为什么呢?

原因在在于:客户端收到FIN后,仅仅表示对方不在发送数据了。并不能代表我不能发数据
给对方。
如果我们给对方发数据,而对方的进程已经不存在了,会导致TCP重置。对方TCP协议栈会
发送RST数据段给我,如果我们再次进行写入操作就会产生SIGPIPE信号。

接下来我们演示 SIGPIPE信号产生,。

首先,打开服务器,服务器处于监听状态

 

打开服务器 两个已连接状态

杀死服务器端处理客户端的进程。。服务器处于FIN_WAIT2 客户端CLOSE_WAIT

在客户端随便输入
得到输出

捕捉到了sigpipe信号。并且client close 还能输出。为什么还能输出呢??这是因为我
们捕捉了这个信号,但是并没有做任何吹,没有将进程终止掉。但是这个信号的默认处理
是将进程终止掉。

通常我们处理 SIGPIPE信号,忽略即可。所以对我们的程序来说,不需要捕捉它,忽略它
即可。signal(SIGPIPE,SIG_IGN);即可

 1 int main()
 2 {
 3     /*
 4     signal(SIGPIPE,handle_sigpipe);
 5     */
 6     signal(SIGPIPE,SIG_IGN);
 7     /*创建套接字*/    
 8     int sockfd;
 9     if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
10         printf("socket err");
11 
12     /*serv addr and port*/    
13     struct sockaddr_in servaddr;
14     memset(&servaddr,0,sizeof(servaddr));
15     servaddr.sin_family = AF_INET;
16     servaddr.sin_port = htons(5188);
17     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
18     
19     /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上
20       是与服务器端的conn套接字连接*/    
21     if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0)
22         printf("connect err");
23     struct sockaddr_in localaddr;
24     socklen_t addrlen = sizeof(localaddr);
25     if(getsockname(sockfd,(struct sockaddr*)&localaddr,&addrlen)<0)
26         ERR_EXIT("getsockname");
27     printf("ip=%s port=%d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));    
28 
29     echo_cli(sockfd);
30 
31     return 0;
32 }

 

posted @ 2017-03-27 21:42  ren_zhg1992  阅读(176)  评论(0)    收藏  举报