【计算机网络】一文读懂TCP:从三次握手到拥塞控制的全面指南

目录

引言:TCP的重要性与核心价值

TCP简介

TCP核心特性 

1)面向连接

2)点对点

3)提供可靠交付

4)全双工通信

5)面向字节流

TCP报头

TCP核心机制

确认应答

超时重传

快速重传

延迟应答

捎带应答

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

三次握手(建立连接)

四次挥手(释放连接)

流量控制——滑动窗口

拥塞控制——拥塞窗口

拥塞窗口

拥塞控制变化曲线

慢启动

拥塞避免

快重传

快恢复

TCP粘包

发送方原因

接收方原因

解决方案

TCP设计哲学

结语


引言:TCP的重要性与核心价值

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

TCP简介

TCP,全称Transmission Control Protocol。其复杂度和UDP相比,它俩根本不在一个维度上,TCP要甩UDP好几条街。因为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时,表明紧急指针字段有效。它告诉操作系统次报文中有紧急数据,应尽快发送,而不是按照原来的排队顺序发送。

举个例子,主机A给远端的服务器B发送了一个程序,使其在B上运行,但是后来发现这个程序存在一些问题,需要及时制止——Ctrl + C干掉这个进程,如果没有URG这个标志位,那么这条指令将存储在TCP接收缓存的末尾,直到前面的数据处理完毕,这样的话就浪费了很多时间。但是如果有URG并将其置1时,发送应用进程就告诉发送方的TCP有紧急数据要发送,于是发送方的TCP就把紧急数据插入到要发送报文段数据的最前面。

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的机制时,不能只盯着某个点,而应该看到这些机制的内在联系,比如不同的机制之间是如何协同,进而使得TCP更高效的。

确认应答

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

我们知道,主机A发给主机B的数据可能会丢失,主机B发给主机A的确认应答也可能会丢失。 既然存在丢包可能,如何确保可靠性?

站在主机A的角度,如果它迟迟收不到B的确认应答,那么它是不知道到底是发送出去的数据包丢了还是B发过来的确认应答丢了的。但没关系,A不关心。如果直到超时计时器到期,A还是没收到确认应答,那么会触发A的超时重传,A再次发送数据包。当A接收到了确认应答以后,就说明B已经收到了数据。这样就确保了从A到B的可靠传输。同理,B在给A发送数据时,B也会等A发来的确认应答,如果B未收到A的确认应答,也会进行超时重传,直到B收到确认应答,说明A收到了数据。这样就确保了从B到A的可靠传输。综上,A和B之间的通信就是可靠的。

超时重传

如上图,A在给B发送数据后,可能因为网络拥堵等原因,数据无法到达主机B。补充一点,A在给B发送数据后,会启动一个叫超时计时器的东西,相当于在倒计时,如果A不能在倒计时结束之前收到B的确认应答,那么就会触发A的超时重传。这个倒计时的时间就是超时重传时间RTO(Retransmission Timeout)。 但是,主机A未收到B发来的确认应答,也可能是ACK丢了。这就引出了新的问题——由于A重传数据包,导致B收到重复数据。B怎么办?这时候B会根据序号判断出收到的数据包重复了,然后丢弃重复数据,并给A再次发送确认应答。

快速重传

快速重传的触发条件是:发送方连续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(发送等待)状态。

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

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

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

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

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

至此,三次握手完成。

四次挥手(释放连接)

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

流程分步:

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

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

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

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

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

4)第四次挥手:客户端收到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的设计哲学体现了折中与平衡,通过牺牲一些实时性来换取可靠性,通过合理的复杂度来换取性能的提升。

结语

不愧是TCP,六千多字还不足以描述其更多细节。为了控制文章的篇幅,我姑且写到这吧。TCP中的更多尚未在上文体现的细节,我会专门再写一篇博客,包括三次握手四次挥手的中细节,以及流量控制和拥塞控制的区别,同时对TCP进行总结。所谓的总结,就是以更全局的视角来解读和理解TCP,比如TCP中涉及到了好几个计时器,文中会把它们放到一起进行对比。待我完成后会把链接贴到下方,需要的自取哈!最后,如有错误,欢迎指出,感谢道友们的理解与支持!

【计算机网络】TCP协议技术细节全解析:与UDP的核心差异深度对比-优快云博客


完~ 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值