浅谈3次握手与4次挥手
浅谈3次握手与4次挥手
TCP内部需要维护一个通信通道的全部相关信息,所以TCP内部抽象出一个对象(Connection)来进行连接管理。
如何判断接收到的数据Segment是否正确?
从TCP内部通过五元组获取连接对象 Connection,进而查看连接的状态信息Connection.state来确定是否正确。
在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接
当SYN标识位置1时,说明***SN用来同步信息的。当ACK置1时,说明确认***ASN是用确认信息的。
三次握手过程:
三次握手四次挥手过程:
客户端状态转化:[CLOSED -> SYN_SENT]
客户端调用connect, 发送同步报文段;[SYN_SENT -> ESTABLISHED]
返回connect, 则进入ESTABLISHED状态, 开始读写数据;[ESTABLISHED -> FIN_WAIT_1]
客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1;[FIN_WAIT_1 -> FIN_WAIT_2]
客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段;[FIN_WAIT_2 -> TIME_WAIT]
客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;[TIME_WAIT -> CLOSED]
客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态.
服务端状态转化:[CLOSED -> LISTEN]
服务器端调用listen后进入LISTEN状态, 等待客户端连接;[LISTEN -> SYN_RCVD]
一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文.
[SYN_RCVD -> ESTABLISHED]
服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了.
[ESTABLISHED -> CLOSE_WAIT]
当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT;[CLOSE_WAIT -> LAST_ACK]
进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)[LAST_ACK -> CLOSED]
服务器收到了对FIN的ACK, 彻底关闭连接.
状态变化:
1 TIME_WAIT: 只出现在主动结束的一方
为什么TIME_WAIT的时间是2MSL?
(1)MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
(2)在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK);
2 CLOSE_WAIT: 只出现在被动结束的一方
一般而言,对于服务器上出现大量的CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭socket, 导致四次挥手没有正确完成. 这是一个BUG. 只需要加上对应的close 即可解决问题
3 TCP异常情况
进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接
4 为什么TCP建立连接是三次握手,不是两次也不是四次?
同步信息的过程两端都需要进行确认ISN,以确认两端都具有收发信息的能力,三次握手能够保证TCP连接的需求,而两次肯定是不可行的。四次握手变成三次,提高效率,节省时间。
参考:
https://cloud.tencent.com/developer/article/1417777
5 为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文, 因此不能一起发送。故需要四步握手
需要四次挥手,socket套接字是全双工的,一个收一个发,一共两个通道,都需要进行关闭。
注意:
客户端可以先close是因为,客户端不绑定固定端口,再次调用时,系统会重新分配端口,而如果服务器先调close,最后资源保留2-5分钟,再次运行时,将会报错(Address already in use),因为服务器绑定固定端口,此时的资源未被释放(端口仍被占用)。