计算机网络 - TCP

基础

根据之前网络通信分层结构的介绍, 我们知道TCP是位于传输层的协议。为什么要分层,不就是为了“各司其职,更加灵活”嘛。应用层,负责制造数据出来;网络层,负责找到数据要发到的正确地方;而中间的传输层则是为了保证数据交到网络的“效率”。

TCP的定义:面向连接的,可靠的流式传输协议。以下我们将会围绕TCP的定义来介绍。

面向连接

TCP定义的第一条就是面向连接,也就是说通信双方得先建立连接才能继续后续步骤。经典的三次握手,四次挥手,就是TCP协议建立连接/终止连接的方式:
三次握手

  • 为什么需要三次握手
    其实就是为了保证通信双方都能确定两边的发送和接收都是正常的。
    为什么要三次握手
  • 为什么要四次挥手
    因为TCP协议是全双工的,也就是说连接建立好之后,两边都可以同时发送数据,接收数据。如果不经过四次挥手,只由一方发送结束连接,另一方确认的话,就会进入半关闭状态,即被动关闭方还能向主动关闭方发送数据,只有四次挥手后,才会完全关闭。
    TCP半关闭的例子

流式传输

应用程序交给TCP的数据是结构化数据,但TCP把它们都当做无结构的字节流来传输。

滑动窗口

传输的方式,是用了一种“滑动窗口”的策略。

从《TCP/IP 详解》一书中摘取的例子🌰:假设需要从svr4传输8192个字节到bsdi
svr4传输8192字节到bsdi
滑动窗口示例

第一张图是整个数据报文发送的过程,第二张图是其对应的滑动窗口移动的示意图。

能够看出,滑动窗口的大小是由接收方进行通告的,代表发送方能发送的数据大小。而发送方不必发送全窗口大小的数据,例如窗口是4096,但发送方的一次数据报文只有1024个字节。接收方ACK数据后,窗口的左边沿会向右移动,代表左侧所有数据都被ACK了。接收方通告的窗口大小是基于ACK的,也就是说如果左边沿向右移动了,窗口大小仍然不变,相当于右边沿也向右侧移动了。

拥塞窗口 & 慢启动 slow start

在上述滑动窗口的描述中,发送方能发送的数据最大值是接受方通告的窗口大小;但是加上慢启动的优化, 发送方能发送的数据最大值其实是通告窗口和拥塞窗口的最小值。

多了一个拥塞窗口 cwnd

之前的方式如果在同一个局域网中没有什么问题,但是在存在多个路由器,存在速率慢的链路的网络中,就会有问题。就像很堵车的时候,可能就会限行,否则一个车也走不了了。拥塞窗口的存在就是为了防止网络过载。

通告窗口是接收方使用的流量控制,与接收方在该连接上的可用缓存大小有关,拥塞窗口是发送方使用的流量控制,是发送方感受到网络拥塞的估计。

机制

建立TCP连接的时候,cwnd被初始化为1个报文段,每收到一个ACK,cwnd就增加一个报文段(即指数式增长,发送1个报文段,收到ACK,cwnd+1, 发送两个报文段,收到2个ACK, cwnd+2,发送四个报文段...),这就是cwnd的窗口大小。

慢启动的🌰:发送方在处于慢启动的时候必须要等到ACK后才能继续发送,因为cwnd此时为1。
慢启动的例子

拥塞避免

拥塞避免和慢启动通常在一起实现。但是他们的目的是不一样的。慢启动是为了在拥塞发生时,控制发送数据的速率,让进入网络中的分组少点。而中间路由器可能有个负荷的极限,超过这个极限会丢掉分组。也就是说当分组丢失的时候,可以用来表征此时网络中发生了拥塞,拥塞避免就是来处理丢失分组的方法。

cwnd完整版

拥塞避免和慢启动需要对每个TCP连接维持两个变量,一个拥塞窗口cwnd和一个慢启动门限ssthresh,其工作流程可视化描述如下:

慢启动和拥塞避免的可视化描述

(ps 看书看了半天文字描述,感觉越来越混乱,还是看图好理解一点)。

图示里,慢启动门限ssthresh设置为16即假定cwnd为32个报文段的时候会发生拥塞。图示0<t<=4的时候,TCP处于慢启动阶段,其cwnd的增大遵循我们上述的规则,收到一个ACK就+1(呈指数增大)。当cwnd达到ssthresh的时候,此时最多一个往返时间里,cwnd+1而不是一个ACK就+1,此后cwnd呈线性增加。

ssthresh的值:当拥塞发生时(超时或者收到重复ACK),被设置成当前窗口大小的一半。

TCP粘包拆包

TCP只是保证传输的,应用层交给TCP的结构化数据在TCP看来都是无结构的字节流。那么就有可能,应用层眼里的两条消息,TCP是一起发送出去的(粘包)。解决方式:

  • 规定长度并补齐:发送端将每个包都封装成固定的长度,比如100字节大小。如果不足100字节可通过补0或空等进行填充到指定长度;
  • 规定边界:发送端在每个包的末尾使用固定的分隔符,例如\r\n。如果发生拆包需等待多个包发送过来之后再找到其中的\r\n进行合并;例如,FTP协议;
  • 规定结构并读取长度:将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
  • 通过自定义协议进行粘包和拆包的处理。

可靠性

超时重传

TCP提供可靠的传输层协议,可靠的保证,就是发送数据后会收到一个确认ACK(参考滑动窗口部分)。但是ACK也可能会丢失,那怎么解决呢?TCP通过一个定时器来解决,如果超时还没有收到ACK,就重传该数据。一个简单的超时重传🌰如下图所示,括号里的是两次报文的时间间隔,可以看到在第7到18行的重传过程中,时间间隔从1s,3s,6s,12s,24s,48s到多个64s,这种x2时间重试被叫做指数退避(exponential backoff)。从第6行首次传到19行复位中间间隔了差不多9分钟,该时间在TCP中是固定的。至于重传定时器如何确定需要重传,RTT(给定连接的往返时间)怎么计算的之类的,这里就不做了解了。

超时重传的例子

快重传 & 快恢复

当接收方收到不按顺序的数据段时,他会立刻返回重复的ACK给发送方,发送方收到三个重复ACK后,会立刻重传这些丢失的数据段,如下图所示:

快重传和快恢复

posted @   rachel_aoao  阅读(145)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示