问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

一文读懂TCP:从三次握手到拥塞控制的全面指南

创作时间:
作者:
@小白创作中心

一文读懂TCP:从三次握手到拥塞控制的全面指南

引用
CSDN
1.
https://blog.csdn.net/bit_pan/article/details/146356971

TCP(传输控制协议)是互联网数据传输的基石之一。在OSI模型中,它属于传输层协议,负责端到端(End-to-End)的可靠通信,是构建HTTP、HTTPS、FTP等应用协议的底层支撑。如果说IP协议是互联网的“公路网”,负责将数据包从源地址送到目标地址,那么TCP就是公路上的“物流系统”,确保包裹(数据)完整、有序、无误地送达。

TCP核心特性

  1. 面向连接:应用程序在使用TCP之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。

  2. 点对点:每一条TCP连接只能有两个端点。

  3. 提供可靠交付:通过TCP连接发送的数据,无差错、不丢失、不重复,并且按序到达。

  4. 全双工通信:双向独立的字节流通道,在发送数据的同时也可以接收数据。

  5. 面向字节流:TCP把应用程序交下来的数据仅仅看成是一连串无结构的字节流。

TCP报头

TCP报头中包含很多字段,这些字段与TCP中的机制息息相关,在了解这些机制之前,我们有必要先谈一谈TCP报头各个字段的含义。

  1. 源端口和目的端口:各占2字节,TCP的分用是通过端口实现的。

  2. 序号:4字节,报文所发送的数据部分的第一个字节,确保数据按顺序交付并检测重复包。

  3. 确认号:若确认号为N,则到序号N-1为止的数据均已被正确收到。

  4. 数据偏移:占4位,TCP报文首部长度,基本单位是4字节。

  5. 保留:占6位。

  6. 紧急URG(URGent):当URG = 1时,表明紧急指针字段有效。它告诉操作系统次报文中有紧急数据,应尽快发送,而不是按照原来的排队顺序发送。

  7. 确认ACK(ACKnowledgment):只有当ACK被置1时,确认号字段才有效。

  8. 推送PSH(Push):若为1,要求接收方立即将数据推送给应用层(避免缓存延迟)。

  9. 复位RST(Reset):若为1,表明TCP连接中出现严重差错,必须释放连接,然后在重新建立连接。携带RST标识的称为复位报文段。

  10. 同步SYN(Synchronize):若为1,表明请求建立连接(三次握手时出现)。

  11. 终止FIN(Finish):若为1,表明请求终止连接。

  12. 窗口:占2字节,[0, 2^16 - 1]之间的整数,接收方通过此字段告知发送方它可以接收的数据量(避免接收缓冲区溢出导致丢包)。以字节为单位,所以接收窗口最大为65535字节(可通过选项调节)。

  13. 检验和:头部+数据的校验值(防止传输错误)。

  14. 紧急指针:当URG=1时有效,指示紧急数据末尾的字节位置x,x - 序列号即可知道紧急数据的字节数。

TCP核心机制

TCP要做到可靠交付,它就必须采取措施来确保可靠性。但如果仅仅只是确保可靠性,TCP也不至于这么复杂。实际情况是,TCP在确保可靠性的基础上,还以合理的复杂度来换取性能的提升。这两个因素共同作用,才导致了TCP很复杂。其次,我们在学习TCP的机制时,不能只盯着某个点,而应该看到这些机制的内在联系,比如不同的机制之间是如何协同,进而使得TCP更高效的。

确认应答

所谓确认应答,就是接收方在收到发送方发来的数据后,发送一个确认报文(ACK)告知发送方——我已经收到数据啦!

超时重传

如上图,A在给B发送数据后,可能因为网络拥堵等原因,数据无法到达主机B。补充一点,A在给B发送数据后,会启动一个叫超时计时器的东西,相当于在倒计时,如果A不能在倒计时结束之前收到B的确认应答,那么就会触发A的超时重传,A再次发送数据包。当A接收到了确认应答以后,就说明B已经收到了数据。这样就确保了从A到B的可靠传输。同理,B在给A发送数据时,B也会等A发来的确认应答,如果B未收到A的确认应答,也会进行超时重传,直到B收到确认应答,说明A收到了数据。这样就确保了从B到A的可靠传输。综上,A和B之间的通信就是可靠的。

快速重传

快速重传的触发条件是:发送方连续3次收到相同的应答。上图的情况就触发了快速重传,快速重传的意义在于它无需等待超时计时器到期,直接就可以补发丢失的数据包,降低了传输延迟,提升网络吞吐量。

延迟应答

首先,TCP是允许不用对所有的报文都进行应答的。一方面,如果接收方每收到一个报文就立即应答的话,这时候返回的窗口值可能会比较小。但如果在等一等上层应用读取接收缓冲区后再返回应答的话,窗口值就会变大一些,发送方就有可能可以发送更多的数据,提升网络的吞吐量,传输效率就越高(在保证网络不拥塞的情况下尽量提高传输效率)。另一方面,如果接收方也有数据要发送,那么数据报文就可以捎带ACK一起发送,减少了小包的数量。但是如果在等待期间,接收方没有数据要发送,它也不会一直等下去,因为存在一个延迟计时器。延迟应答一般每隔2个包就应答一次。下表是延时计时器的工作原理。

启动延迟计时器 在接收到数据包时,如果不需要立即ACK,而且接收方没有数据要发送,那么就会启动延迟计时器(通常默认200ms)。

等待期间 在计时器超时前,如果有数据要发送,那么就捎带数据和ACK,若收到乱序包,立即ACK。

计时器超时 若等待超时后仍没有数据要发送,则单独发送纯ACK报文。

你可能会有这样的疑问:会不会在延迟应答期间导致发送数据的一方因为未收到ACK而进行超时重传?当然不会,因为延迟的时间肯定要在超时重传时间之内的,避免不必要的重发。就算延迟计时器超时了,单独发送的纯ACK报文在到达发送方时,也还没超过超时重传时间。另一个疑问是:快速重传和延迟应答不会冲突吗?一个需要快速触发ACK,一个试图延迟ACK发送,表面上看起来确实很矛盾。但实际上,两者通过场景优先级的划分和协议规则的协同实现了互补。一般是什么情况下需要快速重传呢?是在由于发送方数据丢包而导致接收缓冲区乱序的时候。TCP协议的规则之一就是:乱序包触发即时ACK。收到乱序包时,就会中断延迟计时器,直接就发送ACK报文。下面是延迟应答被中断的几种场景。

  1. 收到乱序包。
  2. 报文中的紧急指针URG被置1。
  3. 超时强制发送。
  4. 窗口更新。若延迟期间窗口增大超过阈值(如50%),立即发送ACK通知发送方。

捎带应答

所谓捎带应答,就是主机A给主机B发送数据,主机B收到后需要给A发送应答,同时B也有数据要向A发送,此时ACK就搭上了数据报文的便车,让搭载数据的报文捎它过去(ACK和数据报文合并)。这样做的好处是:有效避免了网络中小包泛滥。

连接管理——三次握手,四次挥手

首先说说三次握手和四次挥手是干嘛的——三次握手建立连接,四次挥手断开连接。

三次握手(建立连接)

目的:确保双方的发送能力和接受能力均正常。

流程分步:

  1. 第一次握手:客户端给服务器发送SYN = 1的连接请求报文。同时携带自己的初始序号seq = x,客户端进入SYN_SENT(发送等待)状态。

意图:客户端希望建立连接,并告知自己的初始序号。

  1. 第二次握手:服务器向客户端发送SYN+ACK报文,同时携带自己的初始序号seq = y,并对客户端进行确认应答(ack = x + 1),服务器进入SYN_RVCD(同步收到)状态。

意图:服务器同意连接,并告知自己的初始序号。

  1. 第三次握手:客户端收到服务器的确认后,还要向服务器给出确认(ack = y + 1),并携带自己的序号seq = x + 1,客户端进入ESTABLISHED(已建立连接)状态。服务器收到客户端的ACK后,也进入ESTABLISHED(已建立连接)状态。

意图:客户端确认服务端的发送能力正常,完成连接建立。

至此,三次握手完成。

四次挥手(释放连接)

目的:双方有序释放资源,确保数据完整传输。

流程分步:

  1. 第一次挥手:客户端向服务器发送FIN报文,同时进入FIN-WAIT-1(终止等待1)状态。

意图:通知对方不在发送数据,但可以接收数据。

  1. 第二次挥手:服务器收到FIN报文后,进行了ACK,然后进入CLOSE_WAIT(关闭等待)状态,但仍可发送剩余数据。客户端收到服务器的ACK后,进入FIN-WAIT-2(终止等待2)状态。

  2. 第三次挥手:服务器处理完数据后,向客户端发送FIN报文,随后进入LAST_ACK(最后确认)状态。

意图:告诉客户端,我服务器已准备关闭。

  1. 第四次挥手:客户端收到FIN报文后,进入TIME-WAIT状态,并向服务器发送ACK。等待服务器在接收到客户端发来的ACK后立即关闭。客户端等待2*MSL(MSL为报文最大生存时间)后关闭。

至此,四次挥手完成。

流量控制——滑动窗口

接收端处理数据的速度是有限的,如果发送端发的太快,那么接收端的缓冲区很快就被打满。如果发送方继续进行发送,就会造成丢包。因此,TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制。流量控制就是通过滑动窗口来实现的。

以上可以看做是发送方整个发送缓冲区的大小,其中蓝色框框代表的就是滑动窗口,它是发送缓冲区的一部分,就是在发送缓冲区内通过指针维护起来的一部分。滑动窗口内的数据可直接发送,无需等待应答。滑动窗口的大小不得超过接收方的窗口大小。滑动窗口整体是向右移动的,其大小可大可小,也可以为0。当接收方返回零窗口报文时,滑动窗口的大小就为0,这时不能发送数据。这里引出了一个新问题——死锁。

当接收方的接收缓存有了一些空间,那么接收方会给发送方发送一个带有新窗口大小的报文。但是这个报文在传输过程中丢失了。发送方一直等待接受方的非零窗口报文,接收方发出非零窗口报文(丢失)后,一直等待发送方的数据。如果没有其他措施,这中相互等待的死锁局面将一直延续下去。你可能有疑问——接收方不会超时重传非零窗口报文吗?答案是不会。因为TCP规定,窗口更新必须通过带有有效ACK号的报文传输。所以非零窗口报文本质上是一个ACK。TCP又规定,ACK无需确认。也就是说,发出去就完了,不会对ACK进行确认的。因为对ACK进行ACK,也就是ACK的ACK,会导致无限循环。

TCP给出的解决方案是:为每一个连接设有一个持续计时器,只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文(仅携带1字节的数据),而对方就在确认这个零窗口探测报文时给出了现在的窗口值。如果窗口不是零,那么死锁的僵局就被打破了。

滑动窗口通过一次发送多条数据,大大提高了性能,本质上就是将多个报文的等待时间重叠在了一起。

拥塞控制——拥塞窗口

上面我们讲到,TCP有滑动窗口,能够告高效可靠的发送大量数据。但是,如果在刚开始阶段姐贸然发送大量数据,可能会出问题。因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵,在不清楚当前网络状态的情况下,贸然发送大量数据,可能会导致大量超时重传,加剧网络的拥堵。TCP拥塞控制的算法有4种,慢开始(慢启动)、拥塞避免、快速重传和快恢复。

拥塞窗口

拥塞控制,是基于窗口的拥塞控制。这个窗口就叫做拥塞窗口。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化着。前面我们也讲到了一个叫滑动窗口的东西,它用来进行流量控制。输出一个结论:滑动窗口的大小 = Min(对方接收窗口,拥塞窗口)。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以在增大一些,以便把更多的数据发送出去,这样就可以提高网络的利用率。但是,只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的数据,缓解网络的拥堵。

如何判断网络是否出现拥堵或者说预判网络的拥堵?如果出现超时重传,那么发送方就认为网络出现了拥堵。

拥塞控制变化曲线

慢启动

慢启动算法的思想是:在开始发送数据时,先探测一下网络的拥塞情况,由小到大逐渐增大注入到网络中的数据字节,也就是说,由小到大逐渐增大拥塞窗口数值。看上图,一开始拥塞窗口的值设为1,然后指数增长。当增长到阈值ssthresh后,改为执行拥塞避免算法。

拥塞避免

加法增大(AI),按线性规律缓慢增大。改用拥塞避免算法,可以使网络不容易出现拥塞。

快重传

有时,个别报文段会在网络中意外丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会超时重传,并误认为网络发生了拥塞。这就导致发送方错误的启动慢开始,把拥塞窗口值设为1,因而不必要的降低了传输效率。个别丢包以后,如果发送方连续3次收到相同的确认,那么就会触发快重传。及时的把丢失的包补发。

快恢复

发送方检测到只是发生了个别的意外丢包后,不会启动慢开始算法,而是执行快恢复算法。快恢复的起点就是 原来阈值 / 2 的点(乘法减小,MD)。所以虽然随着拥塞窗口的数值越来越大,对滑动窗口的影响其主要作用的已经是接收方窗口的大小了,但是拥塞窗口的值还在增大是有意义的。因为它可以提高快恢复的点,进而更快的恢复到原来水平。加法增大,乘法减小。结合起来就是所谓的AIMD算法。

TCP粘包

所谓的粘包,就是发送方发送的若干数据包到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧挨着前一包数据的尾。出现粘包的现象,既有发送方的原因,也有接收方的原因。

发送方原因

发送方Nagle算法合并小包。把多个小包合成单个包发送。

接收方原因

接收方应用层未及时读取数据导致多个数据包在接收缓冲区中粘连。

解决方案

要解决粘包问题,归根结底就一句话,明确两个包之间的界限。

固定长度法:每个消息都设置为固定长度,不足的部分用特定字符填充。接收方按固定长度读取。

分隔符法:在每条消息的末尾添加特殊分隔符(如换行符)。接收方根据分隔符拆分消息。但需要确保消息内容中不包含分隔符,否则需要转义处理,增加复杂度。

长度字段法:在消息头加上表示消息长度的字段。接收方先读取长度字段,再根据长度读取内容。这是比较常用的方法,像HTTP的Content-Length就是典型例子。

TCP设计哲学

TCP的设计是一场针对不确定性的精心布局:

  1. 对抗网络不可靠性 → 机制化确认、重传、校验。
  2. 适应动态环境 → 流量控制、拥塞控制。
  3. 避免中心化控制 → 端到端原则、通信双方地位平等。

TCP的设计哲学体现了折中与平衡,通过牺牲一些实时性来换取可靠性,通过合理的复杂度来换取性能的提升。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号