1 .服务器
服务器的连接状态变化:
listen 状态: 服务器调用系统调用listen函数,处于监听状态
SYN_RCVD状态 : 收到客户端发送的SYN报文之后,发送SYN报文段的ack响应,进入SYN_RCVD状态
ESTABLISHED状态: 收到客户端的连接建立ack响应报文段,进入ESTABLISHED状态 :三次握手成功,故该状态是双方能够进行双向数据传输的状态
服务器的关闭状态变化:
CLOSE_WAIT:客户端主动关闭连接,发送fin报文段,服务器发送ack响应后进入CLOSE_WAIT状态 :当客户端收到响应后,连接进入半关闭状态,即客->服 连接断开,而服->客 连接仍然存在,客户端仍然能收到服务器的数据
LAST_ACK : 服务器发送给客户端fin报文段,进入该状态:
CLOSED: 正式结束双方的连接
2. 客户端:
客户端的连接状态变化:
SYN_SENT : 在客户端给服务器发送一个syn同步报文段的同时进入该状态
ESTABLISHED : 收到来自服务器的syn ack报文段,进入已确认的连接状态
客户端的关闭状态变化:
FIN_WAIT_1 ; 客户端主动关闭连接,发送fin报文段给服务器,同时进入该状态
FIN_WAIT_2 ; 客户端收到服务器的fin ack响应报文段,进入fin_wait_2状态: 此时代表的是双方处于半关闭状态
TIME_WAIT : 处于FIN_WAIT_2状态后,当服务器发送fin结束报文段,服务器进入last_ack状态,在客户端给予确认后,进入time_wait状态
主动关闭的一方进入time_wait状态:而不是说只有客户端才能进入time_wait状态
3. TIME_WAIT:
客户端连接在收到服务器的结束报文段之后,并没有直接进入CLOSED状态,而是转移到TIME_WAIT状态,这个状态下,客户端连接要等待一段长尾2MSL的时间,才能完全关闭
MSL:报文段在网络中的最大生存时间:2min
TIME_WAIT 存在原因:
1. 可靠的终止TCP连接。 : 若上图中报文段7在网络传输过程中丢失,那么服务器就会重新发送结束报文段,因此客户端需要停留在某个状态以处理重复收到的结束报文段
2. 保证让迟来的TCP报文段有足够的时间被识别并丢弃 :若不存在TIME_WAIT 状态时,则应用程序就能够立即创建一个相似的新的连接(相同IP和PORT),新的链接可能收到来自属于原来的连接的TCP报文段, 这种情况有可能发生,但不应该发生。 因为TCP报文段的最大生存时间是MSL,所以坚持2MSL时间的TIME_WAIT状态能够保证网络上两个传输方向上尚未被接收到的,迟到的TCP报文段都已经消失,因此一个新的连接可以在2MSL时间之后安全的建立,而不会接收到曾经连接的数据,time_wait就是来处理这种情况的。
注意:
一个TCP端口不能被同时打开两次或以上,当一个tcp连接处于TIME_WAIT 状态时,我们就无法立即使用该链接占用着的端口来建立新的连接
4. 针对处于TIME_WAIT下的立即重启客户端或服务器问题:
客户端一般都是由系统自动分配的临时端口号来建立连接,具有随机性,所以尽管处于time_wait状态下,仍然可以立即重新启动客户端,因为与上次所使用的端口号不同
服务器的端口号都是绑定的,不具有随机性,所以若服务器主动断开后其处于time_wait下,就不能够马上重启,解决这个方法可以通过socket选项SO_REUSEADDR来强制进程立即使用处于TIME_WAIT状态的连接占用的端口。
代码一般是在创建套接字之后加一句:
int opt = 1;
int stat = setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));