TCP三次握手、四次挥手全过程(状态和具体的报文)
三次握手:
(1)首先是服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接
(2)客户端通过connect系统调用主动与服务器建立连接,发生同步报文段SYN,使自己转移到SYN_SENT状态
connect系统调用失败的两个原因:目标端口不存在或者被占用;目标端口虽然存在,但在超 时时间内未收到服务器的ACK
(3)服务器一旦监听到有连接请求,就将该连接放入内核等待队列,并向客户端发生SYN、ACK,此时该连接 处于SYN_RECV状态
(4)客户端收到服务器的SYN、ACK,connect调用成功返回并向服务器端发送ACK,连接进入ESTABLISH状态
(5)服务器接收到客户端的ACK,连接转移到ESTABLISH状态
四次挥手
(6)当客户端主动关闭连接时,向服务器发送FIN结束报文段,同时连接进入FIN_WAIT1状态
(7)服务器接收到客户端的FIN,返回ACK同步报文段使连接进入CLOSE_WAIT状态
(8)服务器检测到客户端关闭后会发生FIN报文段,同时使连接进入LAST_ACK状态
(9)客户端接收到到服务器发送的FIN报文段,向服务器发送ACK并进入TIME_WAIT状态。
(10)服务器进入CLOSE状态
为什么不能是是四次握手?
若是四次握手,服务器先向客户端发送ACK,再发送SYN,这样做浪费网络带宽,效率差。这两个步骤是可以合并的。
为什么是四次挥手?三次不行吗?
因为TCP连接是全双工通信。就算服务器收到了客户端的FIN,也只是说明客户端不会再发数据过来,服务器还是可以向客户端发送数据的。四次挥手中的前两次挥手只是关闭了一个方向的数据传输。加上后面的两次挥手才是完整的关闭了整个全双工连接。
假如当服务器处于ESTABLISH状态时,客户端想关闭连接,于是发送FIN,客户端进入FIN_WAIT1状态。服务器会回复ACK之后,客户端进入了FIN_WAIT2状态。FIN_WAIT2表示半连接。也就是说客户端请求关闭连接但是服务器说我还有一些数据要发送,稍后会关闭。
TIME_WAIT状态存在的原因?/对TIME_WAIT状态的理解?
客户端要关闭连接时,并没有直接进入CLOSE 状态,而是先进入TIME_WAIT状态,等待2MSL(报文段最大生存时间)的时间,建议值是2min。
有两个原因:
(1)要保证迟来的数据能被识别并丢弃。因为迟来的数据可能是上一次TCP通信的数据,但是服务器端并不知道这些,接收之后可能会引起数据错乱。
(2)保证可靠的终止TCP连接。假设客户端关闭时,向服务器发送最后一个ACK,这个ACK在途中丢失。由于TCP的重传机制,服务器会再次向客户端发送FIN,所以在客户端接收到FIN之前必须维护这条连接。
TIME_WAIT为什么要持续2MSL的时间?
因为要确认两个传输方向上的尚未被收到的,迟来的TCP报文段被识别并丢弃。这样,一个新的连接可以在2MSL时间后安全的建立,绝对不会接收到属于上一次连接的数据。
TIME_WAIT 和CLOSE_WAIT的区别?
TIME_WAIT是服务器主动关闭连接时形成的,主要是防止最后一个ACK丢失。由于TIME_WAIT要等2MSL的时间,因为服务器端应该尽量的减少关闭连接。
CLOSE_WAIT是服务器被动关闭时形成的。TCP四次挥手时,服务器收到客户端请求关闭连接的FIN,服务器进入CLOSE_WAIT状态,如果服务器不执行close(),服务器就无法进入LAST_ACK状态,那么系统中会存在很多CLOSE_WAIT的连接。此时可能是服务器忙于其他读写操作,所以没有及时的close()。
TIME_WAIT状态如何避免?
服务器可以设置SO_REUSEADDR套接字来通知内核,如果端口忙,但是tcp连接位于TIME_WAIT状态时可以重用端口。比如我聊天室项目中,每次服务器停止工作后,我想使用统一端口重新启动服务器,此时都是绑定失败。就可以通过设置SO_REUSEADDR套接字来解决。
解决方法:在bind之前设置套接字选项:
const int reuse = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
此外,我们也可以通过修改内核参数 /proc/sys/net/ipv4/tcp_tw_recycle 来快速回收被关闭的socket,从而使得TCP连接根本就不进入TIME_WAIT状态,进入运行应用程序立即重用本地的socket地址
为什么是客户端先断开,可不可以服务器先断开?
可以,可以是客户端先关闭,也可以是服务器先关闭,也可以两个同时关闭。但是若服务器先断开,会产生大量TIME_WAIT,浪费服务器资源。