在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接。在之前的博客中,我们已经介绍过了TCP三次握手以及四次挥手的过程,现在我们就这个问题,从传输层的角度来探讨一下。
- 三次握手
(1)客户端向服务器发送SYN信号,请求建立连接;
(2)服务器端接收到客户端的请求后,发送SYN+ACK信号,表示确认建立连接;
(3)客户端收到服务器端的反馈后,向服务器端发送ACK字段表示确认,同时状态变为ESTABLISHED,表示连接建立。
- 四次挥手
(1)客户端向服务器发送FIN信号,表示通知对方,本端要关闭了;
(2)服务器端收到客户端的信号后,发送ACK信号表示确认;
(3)服务器端向客户端发送FIN信号,表示通知对方,本端也要关闭了;
(4)客户端收到后,也反馈给服务器端一个ACK信号,表示确认。
需要注意的是,在理解TCP三次握手以及四次挥手时,我们不仅要理解它们所发的字段的含义及意义,还要注意服务器端和客户端的状态改变。
在之前博客的讨论中,我们已经解释过了为什么是三次握手和四次挥手。
三次挥手的优点:
(1)保证服务器安全性;
(2)成功率极高;
(3)三次握手不一定会百分百的成功,它也有可能会失败,但是失败对于服务器端来说毫无影响,对客户端有影响,客户端可以通过发送RST重新建立连接。
注:TCP建立与释放连接是基于状态机的。
下图为TCP状态转换的一个汇总:
图中较粗的虚线代表服务端的状态变化情况;较粗的实线代表客户端的状态变化情况。
CLOSED是一个假想的起始点,不是真实状态。
- 理解TIME_WAIT
我们先来做一个测试:先启动server,然后启动client,用Ctrl+C终止server进程,再马上运行server,结果如下所示。
这是因为,虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端口。我们可以用netstat命令查看一下:
TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(报文最大生存时间)的时间后才能回到CLOSED状态。
我们使用Ctrl+C终止了server进程,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。
MSL在Centos7上默认配置的值是60s。
我们可以通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看MSL的值:
从TIME_WAIT->CLOSED,客户端要等待一个2MSL的时间,才会进入CLOSED状态。
那么为什么TIME_WAIT的时间是2MSL呢?若客户端最后一次发送ACK给服务器端,所用时间最多为MSL。我们分两种情况来讨论:
(1)当服务器端收到ACK,则断开连接,此后的MSL时间里客户端不会再收到信号;
(2)当最后一次ACK丢失,服务器端没有收到,此时服务器端会重新发送FIN,这期间所用的总时间小于2MSL。
- 解决TIME_WAIT状态引起的bind失败的方法
使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。