Linux网络之TCP协议

文章目录

TCP协议全面讲解

TCP报文格式与基本概念

TCP(传输控制协议)协议作为传输层的核心协议,其设计目标是提供可靠的、面向连接的字节流服务,确保端到端的可靠通信,负责将数据从源端送到目的端,并提供差错恢复和流量控制。

TCP的特点包括 面向连接,可靠,面向字节流,这些特点都依赖于TCP报头中的各个字段。

TCP报文格式

前20字节(假设4字节为一行,即前5行)为TCP报文的基本报头,任何一个TCP报文都包括该基本报头。

以下为各个字段的粗略解释,他们具体作用会在各种机制中具体说明。

1. 源端口号(16位)与目的端口号(16位)

​ 标识发送和接收数据的进程。IP地址负责将数据送到目标主机,而端口号则确保数据被交付给正确的进程(如浏览器、Web服务器、邮箱客户端)。两者结合(IP:Port)构成一个唯一的网络通信端点,即套接字

2. 序号(32位)

​ 标识本报文段所发送数据的第一个字节在整个数据流中的字节编号。这是TCP可靠性与有序性的基础。它有三个核心用途:

​ 1.构建确认应答机制中的确认序号

​ 2.用来做收到报文的去重

​ 3.保证数据向上交付的时候具有顺序(根据序号排序)

3. 确认序号(32位)

​ 表示接收方期望收到对方下一个报文段的第一个数据字节的序号 。同时,它也是对之前所有已接收数据的确认。

​ 只有当报头中的 ACK标志位为1 时,此字段才有效。

4. 首部长度(4位)

​ 该长度为TCP报头的长度,单位是4字节。因为TCP报头有长度可变的"选项"字段,所以需要此字段来明确报头结束和数据开始的位置。最小值为5(对应20字节标准报头),最大值为15(对应60字节最大报头)。

5. 保留字段(6位)

​ 保留给未来使用,目前必须置为0。

6. 控制位(标志位,各1位,共6位)

用于管理TCP连接和控制数据流。它们是:

  • URG(紧急) :为1时,表示报文中有紧急数据,应优先处理。此时紧急指针字段有效。
  • ACK(确认) :为1时,表示确认序号字段有效。建立连接后,几乎所有报文都置为1。
  • PSH(推送):为1时,提示接收方应立即将数据交付给上层应用,而不是等缓冲区满。常用于交互式应用(如Telnet),以减少延迟。
  • RST(复位) :为1时,表示要求立即强制断开连接,通常用于处理异常或拒绝非法连接。
  • SYN(同步) :为1时,表示这是一个连接建立请求同意建立连接 的报文。用于三次握手过程。
  • FIN(终止) :为1时,表示发送方数据已发送完毕,请求关闭本方向的连接 。用于四次挥手过程。

7. 窗口大小(16位)

​ 告知对方自己接收缓冲区的剩余空间,即对方接下来最多可以发送多少字节的数据。

​ 这是TCP实现流量控制的核心机制。通过动态通告窗口大小,接收方可以控制发送方的速率,防止自己被过快的数据淹没。窗口大小的单位是字节。

8. 校验和(16位)

​ 用于差错检测,校验范围包括TCP报头、TCP数据部分,以及一个伪首部(包含源IP、目的IP、协议类型和TCP长度)。

​ 发送方计算,接收方验证。如果校验失败,接收方会直接丢弃该报文,发送方则会因超时未收到ACK而触发重传

9. 紧急指针(16位)

​ 当URG标志位为1时有效。它指出本报文段中紧急数据的最后一个字节相对于序号字段指示的序号的偏移量。

​ TCP的"紧急数据"机制允许发送方中断对方的数据流,通知其有重要数据(如终端的中断命令Ctrl+C)。但实际中应用较少。

10. 选项(长度可变,最多40字节)

用于支持一些附加功能。
常见选项

  1. 最大报文段长度 :在三次握手时双方通告自己愿意接收的MSS值,以避免在IP层分片。
  2. 窗口扩大因子:用于支持大于16位(65535字节)的窗口,以提高长距离、高带宽网络的吞吐量。
  3. 时间戳:用于更精确地计算往返时间和防止序号回绕。
  4. 填充:选项字段的长度必须是4字节的整数倍,不足部分用0填充。

可靠性机制

确认应答(ACK)机制

​ 发送方每发送一个数据段,接收方需返回一个确认报文(即TCP报头中ACK标志位为1,并且带有确认序号的报文),以表明数据已正确接收。双方均使用此机制,可保证双向通信的可靠性。

​ 发消息的时候,用户调用系统调用接口read,将数据拷贝给操作系统,操作系统根据TCP协议进行发送,并且对方的操作系统在收到后自动发送应答报文,用户无需参与

捎带应答

在应答的时候,同时要发送数据,则可以将应答报文与数据报文合并,将ACK信息附带在数据报文中一并发送。既做应答,也发送数据,从而减少报文数量,提高效率。

这也是为什么协议格式中有两个序号,即序号与确认序号

需注意,TCP三次握手的前两次不能携带数据,因为连接尚未完全建立。

超时重传

当发送方发送一个数据报后,在特定的时间间隔后,没有收到接收方返回的应答报文ACK,则会进行补发数据报。

发送方没有接收到ACK存在两种情况

  1. 数据报丢失,对方未收到,自然不会返回应答报文,那么发送方重新补发数据报

  2. 数据报未丢失,但是接收方回应的应答报文丢失。此时,发送方也无法判定是不是真正的丢失,因此TCP规定,这种情况发送方也认为是数据报丢失。

对于第2种情况,发送方补发数据报后,接收方会收到两次相同的报文,序号是数据报的唯一标识,因此接收方收到的数据报发现有重复后会直接丢弃(去重),同时补发ACK应答报文。这是序号的核心作用之一。

时间间隔如何确定?

​ 这个时间间隔是用来控制补发数据报的参考时间。如果网络状态良好,那该时间应该设短一点,如果网络状态较差,则应多给一些时间让数据报在"路上"消耗,因此在Linux操作系统中,这个时间不是固定的,而是随着网络状态而改变的数。

​ 以500ms为单位,指数增长。第一次的等待间隔为1个单位,也就是500ms,第二次为2*500ms,第三次为4 *500ms,......,第n次为2^(n-1) * 500ms.

如果重传达到一定次数,操作系统则认为是网络或者对方主机出现问题,则会关闭连接,结束发送。

连接管理

TCP协议是面向连接的,双方在通信之前需要建立连接,而不是UDP那样,直接给目的主机发送。

建立连接需要经过"3次握手",以最少的通信次数来建立连接。通信结束后需要断开连接,则需换"4次挥手"来进行连接释放。

三次握手

使用最小的次数来达到验证全双工通信的可行性以及建立双方通信意愿共识,使用3次握手即可达到此目的

具体过程

如上图所示

(1)客户端发送SYN;(客户端说:服务器,我想和你建立连接)

(2)服务器回复SYN+ACK; (服务器说:我收到了你的消息,我也想和你建立连接)

(3)客户端回复ACK。 (客户端说:我收到了你的消息)

此时双方均进入ESTABLISHED状态,即连接建立完成。

​ 在逻辑上,要想建立全双工的通信,必须分别验证双方是否具有接和收数据的功能。发送数据后,若能收到对方的应答,则可以验证自己即可以收到数据,也能发送数据。双方均做一次发送数据和收应答,即可验证双方都具有收发功能,即验证全双工通信的可行性。

​ 这样一来就需要四次握手,但是对于服务器而言,ACK应答报文可以同SYN同步报文合并(捎带应答),形成一个报文,这样就可以减少一次发送数据,从原来的四步变成3步,以最小的次数做到双方连接的建立。并且,双方具有通信的共识(都想建立连接,都同意对方建立连接)。

补充:当客户端建立连接的时候,服务器需要无偿建立连接,也就是说只要有客户端发起建立连接,服务器就必须同该客户端建立连接,而不存在有一方建立连接,另一方不建立链接的情况,这也是ACK与SYN合并的前提。

​ 对于客户端来说,发出SYN后,一旦收到了服务器回应的ACK+SYN,并发出最后一个ACK后,此时客户端就认为连接已经建立好了。但是,服务器此时可能还处于SYN_RCVD状态,直到收到最后一个ACK后转为ESTABLISHED状态。

​ 因为在客户端看来,自己可以发送数据(发送第一次SYN),也可以接受数据(收到ACK),并且服务器发送的SYN自己已经回应。此时客户端状态为ESTABUSHED(连接建立完成状态)。这个状态已经可以向服务器发送通信数据。

最后一个ACK若丢失了怎么办?

​ 若客户端的最后一个ACK丢包了,服务器并未接收到,但是客服端并不知道服务器有没有收到(因为为对于确认应答的ACK报文是不会确认的,也就是没有"确认的确认")。客户端可以正常发送数据报文。

​ 客户端若是发送了数据,被处于SYN_RCVD状态的服务器收到,此时服务器就会明白,最后一个ACK丢失了,因为客户端能发送收据就证明客户端已经认为建立连接了,那么也会一定会发送最后一个ACK,这是也是协议规定的。此时服务器需要进行回应特殊的报头,将RST(连接重置标志位)置为1的报头,该报头会请求重置连接,客户端收到后会将原来的异常连接进行释放(释放本地所有资源,相当于直接重置),然后重新进行3次握手的连接建立。该机制进一步加强了TCP协议的可靠性

此外,RST还用于各种异常连接重新建立的场景:

  1. 连接拒绝:客户端连一个未监听的端口,服务端直接RST拒绝。
  2. 握手ACK丢失:第三次握手ACK丢了,服务端超时后若收到数据则RST回应。
  3. 向关闭的连接写数据:连接已关闭,对方还发数据,收到RST回应。
  4. 进程崩溃:服务进程突然崩溃,操作系统替它发RST清理连接。
  5. 半开连接:客户端掉线后恢复,收到服务器的旧数据包,回RST。
  6. 快速关闭:设置SO_LINGER且超时为0,关闭时直接发RST不挥手。
  7. 包序列异常:收到完全对不上的数据包序列,发RST重置连接。

四次挥手

在双方通讯接受后,需要进行连接的关闭,需要通过四个步骤来关闭连接

具体过程

连接关闭需双方分别发起终止:

(1)客户端发送FIN; (客户端说:我要断开连接了)

(2)服务器回复ACK; (服务器说:我收到了你的断开请求,你断开吧)

(3)服务器发送FIN; (服务器说:我要断开连接了)

(4)客户端回复ACK。 (客户端说:我收到了你的断开请求,你断开吧)

客户端发送FIN报头,服务器收到后回应ACK。此时,可以理解为,客户端要发送的数据已经发送完毕,不在向服务器发送数据,这是一种单向的连接断开。此时,若服务器仍有数据未发送完,可以继续发送,客户端仍可以接受,并且要回应ACK。

如上图所示,处于FIN_WAIT_2状态的客户端,仍然可以接受来自于服务器的数据,因为服务器还没有选择关闭连接

那既然客户端都不在发送数据,回应ACK算不算发送数据呢?

​ 断开连接是由用户决定的,用户调用close接口关闭连接,该操作是在应用层,表示用户要发送的数据发送完毕,不会在向服务器发送应用层数据。断开的并不是底层的数据通道,而是应用层用户拿到的文件描述符对应的缓冲区,准确来说是写缓冲区关闭,仍可以通过该文件描述符通过读缓冲区进行读取数据,而传输层以下都由操作系统管理,与用户无关,操作系统帮用户接收来自服务器的数据,并且返回应答ACK。所以上文所说的客服端要发送的数据指的是应用层用户的数据,而非所有的数据报。

​ 也正因存在一方断开连接,另一方还不可以断开连接的情况,因此4次挥手中的第2,3步骤不可以合并,除非在对方提出断开连接的同时,我也要断开连接,此时可以效仿三次握手,将中间两个报文捎带应答,但这种情况实际上也不这么做,只是理论上可行。

状态解释

  1. CLOSE_WAIT

​ 客户端发送FIN,服务器还未发送FIN,此时服务器处于该状态

​ 对应到用户层代码,即服务器端不调用close关闭文件描述符

​ 服务器端发送了FIN后,状态切换为LAST_ACK

​ 若大量的连接长时间处于CLOST_WAIT,那么会浪费服务器资源,称为fd泄露

  1. TIME_WAIT

​ 主动断开连接的一方,最终要处于TIME_WAIT状态,即发送最后一个ACK后,处于TIME_WAIT状态.在等待一定时长(一般是2*MSL。MSL是TCP报文的最大生存时间)后,转为CLOSED状态,

这样做的目的是为了等待历史报文消散,从而不影响新的连接。

解释:当网络压力较大的时候,先提起断开连接的一方在最后发送ACK后,该ACK可能在网络中传输缓慢,或者在某个路由器中阻塞,甚至超过了超时重传的规定时间,从而重新发送新的ACK,那么在网络中就会积攒一些该数据,而TIME_WAIT状态就是等待这些数据消散,即等待他们到达目的主机,被目的主机接收后处理。

​ 有可能刚断开连接又建立连接,若没有TIME_WAIT等待的2*MSL时间,那么就会立即建立连接,那么若网络中还存在上次连接遗留的数据,此时就会达到,从而对新的连接产生影响

​ 还有一个原因就是防止最后一个ACK丢失,若最后一个ACK丢失了,对方收不到ACK,超时重传FIN。此时主动退出的主机就还不能退出,因为需要重新接收FIN补发最后一个ACK

​ 当服务器作为主动关闭方终止TCP连接时,连接会进入TIME_WAIT状态并持续一段时间,在此期间该连接所占用的通信五元组(包括源IP、源端口、目的IP、目的端口和协议)无法被立即复用。如果服务器需要处理海量的短时客户端连接,并出于管理需要主动清理不活跃会话,就会产生大量的TIME_WAIT状态连接。由于服务器的IP、端口和协议通常是固定的,新到达的客户端连接若其源IP和源端口恰好与某个尚未释放的TIME_WAIT连接重复,就会因五元组冲突而无法建立,从而影响服务器的连接处理能力和端口重用效率。

​ 举个例子,我们在玩英雄联盟的时候,假设腾讯服务器上的相关程序突然挂掉了,那么腾讯需要立即重启该程序,在最短的时间内恢复用户的使用,此时,服务程序挂掉了,但是他所建立的连接使用操作系统维护的,此时可能有大量的连接处于TIME_WAIT状态,那么我们在绑定端口号的时候,就会绑定失败,可以理解为该端口号还在被占用,这带来的后果是灾难性的,因此就需要设置相关属性,让端口地址可以复用,这样就可以做到快速重启服务,减小损失。

高效传输

我们现在知道,TCP协议可以通过确认应答机制来保证可靠性,即发送一个数据,回复一个ACK,在发送数据。那这样实际上相当于在串行传输数据,这样的通信效率是低下的,尤其是在远距离传输的时候。

实际上TCP中,发送数据的时候是一次发多个数据报,并行发送,同时等待他们的应答报文,这样可以大大提高性能(讲多个段的等待时间重叠在一起)

流量控制

​ 接收方通过ACK报文中的窗口字段告知自身接收缓冲区剩余空间,发送方依此调整发送速率,实现流量控制。

1.那么建立链接后第一条数据的发送速度该如何控制呢

​ 在3此握手的过程中已经交换ACK,该ACK中已经携带了双方的窗口大小

在TCP中窗口大小只有16位,则双方接受缓冲区最大为2^16。但现代TCP实现通过窗口缩放选项 可以突破这个限制,实际窗口大小可达1GB左右。

2.若接收方接收窗口大小为0,如何处理?

​ 当接收方的接收了一个数据报后接收缓冲区满了,则剩余空间为0,应答的ACK中窗口大小字段即为0,发送方收到之后就会停止发送,等待接收方缓冲区再次有空间。

​ 接收方的数据从接收缓冲区交给上层之后,接收缓冲区就会有相应的空间剩余出来,但是此时发送方并不知道,因为在发送方等待期间无人通。实际中当接收方接受缓冲区有空间时,会给发送方发送窗口更新通知包,即窗口大小不在为0的报文,可以解决该问题。

​ 若窗口更新通知丢失了,发送方一直收不到,那么在一定的时间后,发送方会发送窗口探测包,来检测接收方时候已经有接收能力。TCP协议通过双方的配合来实现流量控制,提升效率。

3.若接收方上层一直不处理数据,发送方的接收缓冲区一直是满的,那么发送方如何催促对方尽快处理数据呢

​ 使用PUS字段,催促对方快速处理数据。当收到该字段为1的报文是,操作系统则会快速处理接收缓冲区中的数据,来为发送方提供便利。

​ 补充:URG字段,紧急指针有效标志位,当该字段为1的时候,16为紧急指针有效,用于表示正文部分中紧急数据的位置。TCP中,紧急数据的大小只有一个字节,这也称作带外数据,即携带外部信息的数据

拥塞控制

1.什么是拥塞控制?

与流量控制类似,当网络情况不好,处于拥塞状态(网络中存在大量数据),那么该网络中的主机都会减小发送数据的数量,来解决网络拥塞问题。

解决网络拥塞的问题,最大的价值是 使多个使用同一个网络进行通信的主机,有拥塞避免的共识。因为一个主机减少数据发送对于整个网络拥塞问题就是杯水车薪,需要大部分主机都减少发送数量,使的网络中积累的数据快速消散,才可以有效减缓网络拥塞。

2.如何判断网络拥堵呢?

发送方发送了很多的数据,少量数据丢包,我们认为仅仅是触发了超时重传,但当大部分数据丢包,就认为是网络拥堵了,此时发送方会减少数据发送量,并且,该网络中其他主机也使用的TCP协议,他们所处的网络环境也拥塞,也会减少数据发送, 很多主机都减少数据发送量,就可以减缓网络拥塞情况

3.拥塞窗口

拥塞窗口:在网络中并行发送的单次数据量在拥塞窗口之内则不会引起网络拥塞,并行发送的单词数据量大于拥塞窗口则极有可能引起网络拥塞

4.如何确定拥塞窗口大小

​ 由于网络状况是浮动的,那也对应着拥塞窗口的大小是不断变化的,发送方是如何获取网络的实时状态呢?

​ 只能通过不断的尝试获取网络情况,TCP引入慢启动机制,该机制就是在不断尝试,遇到拥堵就减少发送。

慢启动:

​ 以指数增长的数量来作为拥塞窗口的大小,先发1,再发2,再发4,再发8......,指数增长前期慢,可以慢慢减少网络发送,让网络恢复

​ 慢启动策略中,拥塞窗口是指数增长,若一直没有网络拥塞,那么拥塞窗口会随着传输轮次一直快速增长,但是由于指数增长后期增长速度非常快,探测出来的拥塞窗口大概率是不准确的,因此不能让拥塞窗口一直指数增长。

慢启动阈值:

​ 引入一个慢启动阈值,等到拥塞窗口大小超过该阈值的时候,拥塞窗口增长由指数增长转为线性增长(加法增大),直到遇到网络拥塞。此时将慢启动阈值变为拥塞时拥塞窗口大小的一半(乘法减小),拥塞窗口从1开始,循环上一轮的做法,使用慢开始,指数增长,加法增大,乘法减小的拥塞窗口算法策略增长

​ 从指数增长转为线性增长的目的不再是拥塞控制,而是更精细的探测较为准确的拥塞窗口,为了下一轮的拥塞控制做准备。若一直使用指数增长,探测出的拥塞窗口会不准确

综合上述方法,就可以得到控制拥塞窗口的算法,慢开始,指数增长,加法增大,遇到拥塞后阈值乘法减小,拥塞窗口置1。

综合理解:开始的慢启动阈值是上次遇到拥塞后拥塞窗口的一半。在开始发送数据的时候,指数增长前期增速满,缓解网络拥塞情况,同时,增速加大,在网络拥塞后快速恢复通信(快速恢复大量并行发送数据的状态)。拥塞窗口增加到慢启动阈值,则由指数增长变为线性增长,一次一次的去试探,试探网络是否拥堵,此时增加的目的是更精细的探测较为准确的拥塞窗口,为了下一轮的拥塞控制做准备。同时,遇到网络拥塞前所发送的数据量相比一直指数增长后所发的数据量大大减小,一定层度上避免了网络拥塞更加严重

拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免网络造成太大压力的折中方案。

滑动窗口

在流量控制部分,我们了解到可以并行发送一批数据,提高网络传输效率,而这一批数据具体的数量是由接收方回应给发送方的窗口大小,但这样是没有考虑拥塞控制的。

实际中,具体发送报文的数量是综合了流量控制与拥塞控制。滑动窗口是实现流量控制和拥塞控制的基础。

滑动窗口:实际发送的报文数量

应答窗口:接收方回应的ACK中的窗口大小

拥塞窗口:拥塞控制过程中试探出的网络能承受的数据量大小

滑动窗口大小 = min(应答窗口大小, 拥塞窗口大小)

1.发送缓冲区

在理解滑动窗口前,首先需要理解的是发送缓冲区。

发送缓冲区是介于应用层与传输层之间的公共区域。

发送数据过程:

发送方:应用层将数据放入发送缓冲区,TCP协议拷贝数据加上报头交给下层,最后发送。

接受方:下层读取TCP报文,TCP协议处理报头后,将有效载荷方入接收缓冲区,供应用层拷贝读取。

发送方的发送缓冲区与接收方的接收缓冲区在经过网络通信后,其中的数据严格相同。

2.面向字节流

用户向发送缓冲区中写入数据后,什么时候发送,发送多少都有操作系统决定,发送一次后,接收方可以分多次读取,也可以一次读取完。不像UDP的面向数据报,用户写入一次数据报文,UDP协议会将这些数据打成一个数据报,一次发送,而接收方也只能一次读取。

面向字节流就像水流,数据是连续的、无边界限制的字节序列。发送方分多次写入"Hello"和"World",接收方可能一次就读取到"HelloWorld",也可能分三次读到"H"、"elloWor"、"ld"。TCP协议不关心应用层的数据边界,只保证字节的顺序和可靠性,应用层需要自己解决"消息边界"问题。

面向数据报则像快递包裹,每个数据报都是独立的、有明确边界的完整消息。发送方发送三个独立包裹"Hello"、"World"、"TCP",接收方就会收到三个独立的包裹,绝不会出现半个包裹或包裹粘连的情况。UDP协议保留了每个消息的边界,但需要应用层处理丢失、重复和乱序问题。

3.理解滑动窗口

由于TCP协议是面向字节流的,因此发送缓冲区我们可以认为是个数组(实际上是通过指针实现的环形队列)。

发送方就可以在发送缓冲区维护一块连续的区域,称作发送窗口

如上图所示,发送窗口内有发送5个数据段,那么操作系统会发送这5个数据段,期间后3个丢包,那么接收方会接收到其中前两个,并确认应答,此时发送方收到确认应答后,将这两个报文归入已发送已确认,并且窗口大小不变的话,那么将80019000以及900110000这两个数据段加入发送窗口,此时就像发送窗口在右移一样

滑动窗口移动情况:

1.不能向左边移动,因为左边是已经发送并且已经确认了的数据,不会在确认后再次去发送该数据,所以窗口不会向左滑动

2.窗口可以增大,也可以减小

窗口大小取决去对方返回的应答窗口与拥塞窗口,下面根据具体的情况来分析:

无丢包情况
  • 发送方发送:连续发送段1、2、3、4、5。

  • 接收方接收:按序收到段1、2、3、4、5。

  • 接收方回应:每收到一个段,发送对应ACK(ACK2、ACK3、ACK4、ACK5、ACK6)

  • 滑动窗口移动

    • 初始窗口:1,2,3,4
    • 每收到一个ACK,窗口向右滑动。例如收到ACK1后,窗口变为2,3,4,5(假设有数据5)。
    • 最终窗口平稳右移。

图例解释:

最左边丢包
  • 发送方发送:发送段1、2、3、4、5(段1丢失)。

  • 接收方接收:乱序收到段2、3、4、5(缓存),最后收到重传的段1。

  • 接收方回应

    • 收到段2、3、4、5:各发送ACK1(因期望段1,确认号1表示尚未收到任何数据)。
    • 收到重传段1后:累积确认段5,发送ACK6。
  • 滑动窗口移动

    • 初始窗口:1,2,3,4,5
    • 收到多个ACK1后窗口不滑动,触发快速重传段1。
    • 收到ACK6后,窗口左边界移至6,变为6,7,8,9,10
中间丢包
  • 发送方发送:发送段1、2、3、4、5(段3丢失)。

  • 接收方接收:乱序收到段1、2、4、5(缓存),最后收到重传的段3。

  • 接收方回应

    • 收到段1、2:回复ACK2,3
    • 收到段4、5:各发送ACK3(因期望段3,确认号3表示尚未收到任何数据)。
    • 收到重传段3后:累积确认段5,发送ACK6。
  • 滑动窗口移动

    • 初始窗口:1,2,3,4,5
    • 收到ACK1,2后向右滑动
    • 收到ACK4,5保持不变,因为段3尚未确认
    • 收到ACK3后,窗口左边界移至6,变为6,7,8,9,10

可以看到,该情况在左边的段都接收到,然后右移之后,与最左边丢包情况相同。

最右边丢包
  • 发送方发送:发送段1、2、3、4、5(段5丢失)。

  • 接收方接收:顺序收到段1、2、3、4(正确顺序接收,接收方可直接向应用层交付,也可等待段5到来一起交付),最后收到重传的段5。

  • 接收方回应

    • 收到段1、2、3、4:回复ACK2、3、4、5
    • 若后续6、7、8、9发送,这与最左边丢包情况相同,都会回复ACK5,触发快重传
    • 若后续未发送,则一定时间后触发超时重传
  • 滑动窗口移动

    • 初始窗口:1,2,3,4,5
    • 收到ACK2、3、4、5后向右滑动
    • 收到ACK5保持不变,收到ACK6后,变为6,7,8,9,10

可以看到,这也可以转化为最左边丢包的情况

滑动窗口综合理解

发送方视角:

发送方的窗口滑动依赖于接收方的ACK确认:当窗口左侧的数据段被连续确认,窗口就向右滑动,表示左侧数据已可靠送达,右侧新数据可以开始发送。如果发生丢包导致ACK缺失,窗口会卡住,此时发送方通过重传填补空缺;一旦重传数据被确认,窗口便补全缺口,恢复向右滑动,继续发送后续连续数据。

​ 发送方窗口左边界:收到ACK的最大值。解释:确认序号的含义是,我期望收到该序号开始的数据段,该序号之前已经接收,则该序号之前的数据应都在窗口左侧,图例中绿色部分

​ 右边界:在左边界的基础上加上滑动窗口大小

​ 滑动窗口大小: min (应答窗口,拥塞窗口)

接收方视角:

接收方只向上层交付连续的数据:它按顺序接收数据段,若左侧出现缺失,即使后面的数据先到达也会被暂存。接收方会等待缺失的数据段(可能由发送方重传而来),一旦补齐缺口,就从窗口左侧开始将连续数据交付给应用,确保数据有序性和可靠性。并且,数据段的序号落入接收方的接收窗口内才允许收下。若收到的序号在窗口之外则一律丢弃。

相关推荐
大树8811 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠11 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
LDR00611 小时前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术11 小时前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园11 小时前
C++20 Modules 模块详解
java·开发语言·spring
霸道流氓气质11 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush411 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52012 小时前
Linux 11 动态监控指令top
linux
swordbob12 小时前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
源分享12 小时前
Java线程同步的多种实现方法(非常详细)
java·开发语言·jvm