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 }