1、客户端随机初始化自己的序列号x,并且将SYN标志位置为1,发送给服务端。该报文不携带数据。此时客户端处于SYN-SENT状态。
2、服务端收到来自客户端的SYN报文后,为其分配TCP缓存。然后也随机初始化自己的序列号y,同时将确认序列号置为x+1。将控制位SYN和ACK置为1,表示这是个SYNACK报文。然后发送给客户端。这个报文也不携带数据。此时服务端处于SYN_RECVD状态。
3、客户端收到来自服务端的SYNACK报文后,将确认号字段置为y+1,将ACK标志位置为1。发送给服务端。本次报文可以携带数据。三次握手结束后客户端和服务端进入ESTABLISHED(已确立)状态,可以开始发送数据了。
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。
注意:
- ACK报文是不会重传的,当ACK丢失时,就由对方重传相应的报文。
- 重传都有最大重传次数
- 重传时间为上次的2倍
第一次握手丢失
客户端收不到SYN-ACK报文,就会触发超时重传机制,重传SYN报文,且序列号不变。
每次超时的时间是上一次的2倍,Liunx中SYN的最大重传次数默认为5
最后如果还没收到第二次握手客户端就会断开连接。
第二次握手丢失
第二次握手有两个目的:
- 第二次握手里的 ACK, 是对第一次握手的确认报文;
- 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文;
如果第二次握手丢失,客户端就会触发超时重传机制,重传 SYN 报文。并且服务端这边会触发超时重传机制,重传 SYN-ACK 报文。
第三次握手丢失
三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以服务端会触发超时重传机制,重传 SYN-ACK 报。
解决方案:**Syn cookie。**服务器再接收到SYN包并返回TCP SYNACK包时,并不需要分配TCP缓存而是根据这个SYN包计算出一个cookie值。这个cookie作为将要返回的SYN ACK包的序列号。当客户端返回一个ACK包时,根据包头信息计算cookie,与返回的确认号进行对比,如果相同则建立连接。
假设客户端是主动断开连接的一方。
1、客户端向服务端发送连接释放报文,该报文FIN=1,序列号seq=u,此时,客户端进入FIN-WAIT-1(终止等待1)状态。
2、服务端收到连接释放报文后,发送ACK确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v。此时服务端就进入CLOSE-WAIT(关闭等待)状态。
3、客户端收到服务端的确认后,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务端发送连接释放报文。
4、服务端数据发送完毕后,向客户端发送连接释放报文,FIN=1,ACK=1,ack=u+1,seq=w。此时,服务端进入LAST-ACK(最后确认)状态。
5、客户端收到连接释放报文后向服务端发送确认报文,ACK=1,ack=w+1,seq=u+1。此时客户端进入TIME-WAIT(时间等待)状态。
6、服务端只要收到了客户端的确认就立即进入CLOSED状态。
7、客户端经过2MSL后也进入CLOSED状态。
注意:
- u=前面已经传送过的数据的最后一个字节的序号+1
- TCP规定,FIN报文即使不携带数据,也要消耗一个报文段
- 注意,客户端发送第四次挥手后,TCP连接并没有释放,必须等待2MSL(最长报文段寿命)后,客户端才进入CLOSED状态
- 可以看到服务端结束TCP的时间比客户端早
因为TCP是全双工模式,发送FIN报文意义是表示自己不再发送数据了,但是还要接收数据。当被动方收到主动方的FIN报文时,可能还有数据要发送,所以第二次握手和第三次握手不能合并,只能先返回一个ACK报文。
简单来说,前两次挥手用于关闭一个方向的数据通道,后两次挥手用于关闭另一个方向的通道。
第一次握手丢失
客户端迟迟收不到服务端的 ACK,也就会触发超时重传机制,重传 FIN 报文。
第二次握手丢失
ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文。
第三次握手丢失
服务端就会重发 FIN 报文
第四次握手丢失
服务端就会重发 FIN 报文
总结:
- 前两次挥手丢失:客户端重传连接释放报文
- 后两次挥手丢失:服务端重传连接释放报文
TIME_WAIT状态是主动方发送第四次挥手后的状态。
它的作用是确保被动方能够接受到最后的ACK报文,以确保TCP连接正常关闭。
如果客户端最后一次 ACK 报文在网络中丢失了,那么服务端会重发 FIN 报文。假设没有TIME_WAIT状态,当第三次挥手重传到达客户端时,客户端已经关闭。客户端会返回一个RST错误报文。
为了防止这种情况出现,客户端必须等待足够长的时间,确保服务端能够收到 ACK。
客户端在收到服务端重传的 FIN 报文时,TIME_WAIT 状态的等待时间会重置回 2MSL。
因为TIME_WAIT的时间要大于等于客户端收到服务端超时重传的FIN的时间。
假设第四次挥手丢失了:
- 服务端判断需要重传的时间是1MSL。任何报文段在网络中的最大生命周期是1MSL,所以当被动方在1MSL内没有收到ACK,就会重传FIN。
- FIN到达客户端的时间是1MSL。重传的FIN最慢到达客户端的时间是1MSL。
一来一回的时间就是2MSL。
TCP的超时重传时间为RTO,它一般大于TCP报文端的平均往返时间。