为什么TCP建立连接要求发送方和接收方的初始化序列号不一样的呢?

TCP建立连接时,为何发送方和接收方的初始序列号不能相同?本文解释了这一设计的原因,主要是防止历史报文干扰新连接,以及ISN随机生成算法确保几乎不可能重复。

前几天有位读者问我为什么 TCP 建立连接需要三次握手文章中一个问题:

就是他不明白「序列号解决了数据包的缺失和顺序颠倒等问题,但为什么要求发送方和接收方的初始序列号不一样?」

后来,我跟他交流半个小时,终于把他讲明白了。

我觉得应该有不少人会有以下的问题。

1.为什么接收方可以通过序列号对重复的数据包进行去重?

根据上图,发送方向接收方发送seq=1的数据包,接收方收到数据包后,也向发送方发送了ack=seq+1的响应,传输途中发生了丢包的现象。

发送方未收到接收方发送的ACK,在发送方等待了特定时间间隔后依旧没有收到ACK,那么发送方就认为接收方没有收到数据,因此发送方就按照超时重传的方式来处理,如果发生了大量的这样的问题,那么接收方就会收到大量的重复数据。

针对上面出现的问题,TCP需要能够识别出那些重复的包,并把重复的包去掉,这样就必须对包进行去重,可以根据TCP报头中的序列号来进行去重。

2.为什么发送方和接收方的初始化序列号要求不一样的呢?

主要原因是为了防止历史报文被下一个相同四元组的连接接收。

是的,如果能正常四次挥手,由于 TIME_WAIT 状态会持续  2 MSL 时长,历史报文会在下一个连接之前就会自然消失。

但是我们并不能保证每次连接都能通过四次挥手来正常关闭连接。

假设每次建立连接,发送方和接收方的初始化序列号一样,比如都是从 0 开始:

过程如下:

  • 发送方和接收方建立一个 TCP 连接,在发送方发送数据包被网络阻塞了,而此时接收方的进程重启了,于是就会发送 RST 报文来断开连接。

  • 紧接着,发送方又与接收方建立了与上一个连接相同四元组的连接;

  • 在新连接建立完成后,上一个连接中被网络阻塞的数据包正好抵达了接收方,刚好该数据包的序列号正好是在接收方的接收窗口内,所以该数据包会被接收方正常接收,就会造成数据错乱。

可以看到,如果每次建立连接,发送方和接收方的初始化序列号都是一样的话,很容易出现历史报文被下一个相同四元组的连接接收的问题。

那发送方和接收方的初始化序列号都是随机的,那还是有可能随机成一样的呀?

对不起,还真不会。

RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, localport, remotehost, remoteport)。

  • M是一个计时器,这个计时器每隔4毫秒加1。

  • F 是一个 Hash 算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值,要保证 hash 算法不能被外部轻易推算得出。

可以看到,随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。

参考资料

[^1]Why do we need a 3-way handshake? Why not just 2-way?

https://networkengineering.stackexchange.com/questions/24068/why-do-we-need-a-3-way-handshake-why-not-just-2-way

[^2]rfc793

https://datatracker.ietf.org/doc/html/rfc793

为什么这么设计(Why’s THE Design)是一系列关于计算机领域中程序设计决策的文章,我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值