TCP
一、三次握手
TCP 三次握手,其实就是在发送数据前,通过 TCP 协议,跟对方协商好连接信息,建立起TCP的连接关系。
这过程中,客户端与服务器需要交互 3 个数据包。
作用
握手的主要作用,是为了确认双方的接收和发送能力是否正常,初始序列号,交换窗口大小以及 MSS 等信息。
步骤
第一次握手:客户端发送 SYN 报文,并进入 SYN_SENT 状态,等待服务器的确认;
第二次握手:服务器收到 SYN 报文,需要给客户端发送 ACK 确认报文,同时服务器也要向客户端发送一个 SYN 报文,所以也就是向客户端发送 SYN + ACK 报文,此时服务器进入 SYN_RCVD 状态;
第三次握手:客户端收到 SYN + ACK 报文,向服务器发送确认包,客户端进入 ESTABLISHED 状态。待服务器收到客户端发送的 ACK 包也会进入 ESTABLISHED 状态,完成三次握手。
为什么不能用两次握手进行连接?
会发生死锁
服务端无法确定客户端是否已经接收到了自己发送的初始序列号。
如果第二次握手报文丢失,那么客户端就无法知道服务端的初始序列号,
在这种情况下,客户端认为连接还未建立成功,将忽略服务端发来的任何数据分组,只等待确认应答分组。
而服务端在发出的分组超时后,重复发送同样的分组。
这样就形成了死锁。仅两次握手,无法确认双方的收发能力
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
只有三次握手才能互相确认双方的接收与发送能力是否正常。
仅两次握手,无法判断当前SYN报文是否为历史连接
客户端由于某种原因发送了两个不同序号的 SYN 包,旧的数据包有可能先到达服务器。如果是两次握手,服务器收到旧的 SYN 就会立刻建立连接,那么会造成网络异常。
如果是三次握手,服务器需要回复 SYN+ACK 包,客户端会对比应答的序号,如果发现是旧的报文,就会给服务器发 RST 报文,直到正常的 SYN 到达服务器后才正常建立连接。
所以三次握手才有足够的信息,来判断当前连接是否是历史连接。
二、四次挥手
步骤
第一次挥手。客户端发起 FIN 包(FIN = 1),客户端进入 FIN_WAIT_1 状态。TCP 规定,即使 FIN 包不携带数据,也要消耗一个序号。
第二次挥手。服务器端收到 FIN 包,发出确认包 ACK(ack = u + 1),并带上自己的序号 seq=v,服务器端进入了 CLOSE_WAIT 状态。这个时候客户端已经没有数据要发送了,不过服务器端有数据发送的话,客户端依然需要接收。客户端接收到服务器端发送的 ACK 后,进入了 FIN_WAIT_2 状态。
第三次挥手。服务器端数据发送完毕后,向客户端发送 FIN 包(seq=w ack=u+1),半连接状态下服务器可能又发送了一些数据,假设发送 seq 为 w。服务器此时进入了 LAST_ACK 状态。
第四次挥手。客户端收到服务器的 FIN 包后,发出确认包(ACK=1,ack=w+1),此时客户端就进入了 TIME_WAIT 状态。注意此时 TCP 连接还没有释放,必须经过 2*MSL 后,才进入 CLOSED 状态。而服务器端收到客户端的确认包 ACK 后就进入了 CLOSED 状态,可以看出服务器端结束 TCP 连接的时间要比客户端早一些。
为什么建立连接是3次,关闭连接需要4次?
TCP 握手的时候,接收端是将一个 ACK 和一个 SYN 合并到一个 SYN+ACK 包中发送,其中ACK报文是用来应答的,SYN报文是用来同步的。
所以减少了一次包的发送,三次完成握手。
但是关闭连接时,在主动关闭方发送 FIN 包后,接收端可能还要发送数据,不能立即关闭服务器端到客户端的SOCKET。
只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。
然后等服务器无需发送数据时,再发送 FIN 包,
ACK和FIN包不能合并发送。
所以关闭连接时,必须是四次数据包的交互。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间。
TIME_WAIT状态就是用来重发可能丢失的ACK报文。
原因一: 确认服务器收到了客户端发送的 最后一个ACK包。
客户端发送对服务器端的最后一个ACK包有可能丢失,
服务器端如果收不到 ACK 的话,将不断重复发送FIN包。
所以客户端发送 ACK 后需要留出 2MSL 时间
(ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回)
等待确认服务器端确实收到了 ACK 包。
若没收到服务器端的重传包 FIN,
说明ACK已经被成功接收,则结束TCP连接。
原因二: 避免新旧连接混淆。
在客户端发送完最后一个 ACK 报文段后,在 2MSL 时间内,可使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP有一个保活计时器。
客户端如果出现故障,服务器不能一直等下去,浪费资源。
服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时。
若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。
若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
- 本文作者:JSZ
- 本文链接:blog.vampuck.com/2022/03/01/tcp/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!