TCP的三次握手以及四次挥手,丢包怎么办

36 阅读6分钟

对于很多同学来说,已经很清楚了解了TCP建立链接以及传输数据的整个过程,本文重新复习一下细节问题

image.png

三次握手

很简单的过程:

  1. 客户端向服务器发送一个报文请求建立链接,因为浏览器会不止一次向服务器发送报文,并且报文也有可能丢失,所以我们每个报文需要有唯一标识也就是这里的SYN(同步序列编号),比如是x
  2. 在服务器收到报文后,会回复一条报文,同样的机制,服务器收到的不止一条报文,所以为了区分回复的哪一条报文,在报文中会加上明确标识也就是这里的ACK(确认码),确认码的值就是收到的SYN序列号x+1(这是返回需要客户端解析的);除了ACK,此次报文还加上了SYN,比如是y,这个和第一步的逻辑是一样的,是为下一次报文服务的
  3. 客户端收到第二条报文后,对于客户端而言,既可以向服务端发送报文也能收到服务端的确认回复报文,此时客户端的状态就可以设置成established准备数据传输了;但是对于服务器来说,还不确定客户端是否收到了自己的报文,所以此时需要客户端发送第三次报文,此时报文中只需要携带ACK,值是第一次报文中的SYN序列号y+1,服务端收到确认回复包之后确认消息传输畅通就也把状态置为established准备数据接收了

握手过程中包丢失了怎么办

第一次报文丢失,客户端会等待服务端返回确认报文,迟迟没有收到服务端的报文时根据TCP的超时重传机制,比如会等待3秒、6秒、12秒(通常每次超时重传时间是上一次的2倍)后重新发送报文,重传的SYN报文序列号是一样的,重传的次数也是在系统中控制的,一旦超过次数,客户端就会关闭此次连接

第二次报文丢失,此时对于客户端来说迟迟没有收到确认报文,客户端就会认为自己发送的第一次SYN报文丢失了;而对于服务器来说,因为此时报文也包含SYN,所以需要客户端发送ACK确认报文,迟迟没有收到回复就会认为自己的报文丢失了。所以此时客户端和服务端都会触发重传机制

  • 客户端重新向服务器发送SYN报文,也就是第一次握手
  • 服务端重新向客户端发送SYN + ACK报文,也就是第二次握手

第三次报文丢失,第三次报文实际上是客户端对服务器的应答ACK报文,注意ACK报文是不会有重传的,当ACK报文丢失了,就由对方重传对应的报文;所以实际上此次报文丢失就由服务端重新向客户端发送SYN + ACK报文,也就是第二次握手,以便客户端发送ACK报文

为什么不是两次握手

考虑这样一种正常情况:

假设TCP连接是两次握手,当客户端发送一个请求链接报文时,由于网络原因这个报文丢失了,那么客户端一段时间内没有收到服务器的回复报文,就会触发超时重传,此时这次报文服务器接收到了,双方建立正常连接,开始数据传输,最后完成传输关闭链接

但是如果上面说到的报文没有丢失,只是在一个网络节点长时间滞留了,当双方链接关闭后又慢悠悠的到了服务器,此时服务器认为客户端又要建立链接了,由于是两次握手向客户端发送确认报文后就变成了连接状态

可想而知,客户端并不是真正的发送数据,服务器则会一直等待,这样就浪费了服务器资源

image.png

四次挥手

四次挥手的过程实际上和三次握手过程是一样的,只有一点区别就是服务器收到客户端的FIN报文后会先发送一个ACK确认报文,过一段时间后才会发送服务器的FIN报文,这里是不是觉得为什么和TCP连接的第二次握手不一样,为什么需要拆分成两次报文呢。实际上很好理解,客户端的FIN报文到达服务器时,服务器可能还没接收完数据呢,只有接收完数据之后才能发送FIN报文

挥手过程丢包,这个的处理过程和握手没有太大区别

挥手时为什么等待了2MSL之后客户端才关闭连接

MSL为最大报文段生存时间,为什么客户端不能立刻关闭连接而是要进入TIME-WAIT阶段,等待2MSL时间呢,原因有以下两点

保证TCP协议的全双工连接能够可靠关闭

如果客户端直接关闭了,上面说到报文可能发生丢失,如果服务器没有收到第四次握手的ACK报文,超时之后会继续发送FIN报文,由于此时客户端已经关闭了,最后服务端就会收到RST(连接复位)而不是ACK,然后就会以为是连接错误并把问题上报,这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以客户端等待2MSL时间可以确保当再次收到FIN时能够保证服务器收到ACK报文

保证这次连接重复的数据段从网络中消失

如果客户端直接关闭,然后又再向服务器发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达服务器,由于新连接和老连接的端口号是一样的,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2MSL,这样可以保证本次连接的所有数据都从网络中消失。