主要原因是为了防止历史报文被下一个相同四元组的连接接收。
如果正常通过四次挥手完成,TIME_WAIT状态会持续2MSL,历史报文会在下一个连接之前就自然消失,但无法保证每次连接都能通过四次挥手正常关闭。
- 假设客户端和服务端建立一个连接后,客户端发送包被网络阻塞了,然后超时重传了这个数据包,而此时服务端设备断电重启了,之前与客户端建立的连接就消失了,于是在收到客户端的数据包的时候就会发送RST报文。
- 这时候客户端和服务端再次建立相同四元组的连接。
- 在新连接建立完成后,上一个连接中被网络阻塞的数据包正好抵达了服务端,刚好历史数据报文的序列号在服务端接收窗口的范围内,所以该数据包会被服务端正常接收,数据错乱。
如果每次建立连接客户端和服务端的初始化序列号都「不一样」,就有大概率因为历史报文的序列号「不在」对方接收窗口,从而很大程度上避免了历史报文,不是完全可避免
客户端和服务端的初始化序列号都是根据计数器递增的随机数产生的,基本不会产生一样的初始化系列号。
- 序列号:序列号是一个 32 位的无符号数,因此在到达 4G 之后再循环回到 0。
- 初始序列号:客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号。初始化序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加 1,循环一次需要 4.55 小时。
序列号和初始序列号并不是无线递增的会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据,需要事件戳解决tcp_timestamps 便于精确计算 RTT ,另一个是能防止序列号回绕(PAWS)。
防回绕序列号算法,要求连接双方维护最近一次收到的数据包的事件戳每收到一个新数据包都会读取数据包中时间戳值跟Recent TSval值作比较,如果发现收到的数据包中时间戳不是递增的,则表示该数据包是过期的,就会直接丢弃这个数据包。
总结:
客户端和服务端的初始化序列号都是根据计数器底层的随机数,随机生成的,能很大程度上避免历史报文被下一个相同的四元组的连接接收,然后有引入时间戳机制,从而完全避免历史报文被连接接收的问题。
时间戳回绕:
增加时间戳的大小,由32 bit扩大到64bit
将一个与时钟频率无关的值作为时间戳,时钟频率可以增加但时间戳的增速不变