UDP/TCP ④-延时应答 || 捎带应答 || 粘包问题 || 异常处理
这里是Themberfue
上节我们讲完了TCP协议中的几个机制,这节我们将深入更多保证TCP协议可靠性的机制。
延时应答
- 在之前学到的确认应答、滑动窗口和流量控制的机制中,在发送方发送数据包给接收方后,接收方会返回一个ACK确认应答报文表示接收方这边已经收到了发送方发送的数据包,ACK报文通常有确认序号以及16位窗口大小以表示下次发送方从哪个序号开始发送数据和此时接收方缓冲区的剩余空间大小。
- 默认情况下,接收方一接收到发送方发送的数据包后就发送ACK报文;但是,如果接收方不立即发送ACK报文,这样传输的效率会不会更快?答案是肯定的。
- 如果每个发送数据包都要返回一个对应的ACK报文,这样会使传输效率大大下降,再者,如果我接收方等一会再发送ACK报文,接收方这边先处理缓冲区的一些数据包,等一会之后,缓冲区剩余空间大小大概率会变大,那么发送方下一次就可以一次性发送更多的数据了,这将会大大增大传输效率。
- ✨延时应答(Delayed Acknowledgment) 是一种用于优化 TCP 数据传输性能的技术。它通过延迟发送 ACK(确认应答)来减少网络流量和 CPU 资源的消耗,但同时不会影响数据的传输可靠性。
延时应答的工作原理:在普通情况下,接收方会在收到数据后立即发送一个 ACK 确认数据包。但在延时应答机制中,接收方会在以下两种情况中才发送 ACK:
延时超时(通常是 200ms):如果接收方没有更多的数据需要发送,它会在超时时间到达后发送 ACK。这通常发生在数据更加稀疏的场景。
接收到足够多的数据包:如果在延时时间内,接收方又收到了新的数据包,会立即发送一个 ACK,这通常每隔N个包就应答一次。这通常发生在数据更加稠密的场景。
💎通过引入延时应答可以减少网络流量:如果每收到一个数据包就发送一个 ACK,会导致大量小数据包在网络中传输,增加网络开销。延时应答可以将多个 ACK 合并成一个,减少不必要的开销。提高系统性能:发送数据包和处理 ACK 都会消耗 CPU 时间。延时应答通过减少 ACK 数量,降低了 CPU 的使用率。优化小数据传输:在某些场景下,接收方可能会在短时间内发送自己的数据(如小型响应)。延时应答可以将 ACK 和接收方的数据合并成一个包发送,节省带宽。
- ❗不过这也不是说百分之百会提高效率的,从经验来看一定是有帮助的。延时应答也可能带来如下风险:数据传输延迟:延时应答引入了额外的延迟,可能对实时性要求高的应用(如 VoIP 或在线游戏)产生负面影响。误判重传:发送方可能因为接收方的 ACK 延迟而误判数据丢失,从而触发超时重传。复杂性增加:延时应答需要额外的机制来确保超时时间的精确性和多种场景的兼容性。
- ❗总之,应该合理的看待延时应答。平衡好延时应答需要设定合理的延时时间:常见的延时设置为 40ms 至 200ms。如果时间过长,会导致发送方误判丢包。动态调整:根据网络状态和应用类型,动态调整延时时间。例如,实时性要求高的应用可以禁用延时应答。TCP 协议优化:一些协议(如 Nagle 算法)与延时应答结合,可以进一步优化小数据包的传输效率。
- 延时应答通常应用在:流量密集的网络:如 Web 浏览器或文件传输协议,减少 ACK 的数量可以显著优化性能。双向通信场景:在接收方需要立即发送数据的场景,ACK 可以和数据一起发送,进一步减少数据包的数量。传输较大数据块的场景:延时应答在数据块较大时效果更明显,因为 ACK 的总量相对减少。
捎带应答
- 捎带应答是基于延时应答的一种优化机制,本质是将接收方的ACK报文,与接收方的响应数据一起发送给发送方,将这两个包合并在一起可以提高传输的效率。标识为ACK报文通常在TCP首部设置就行(ACK置为1、确认序号、缓冲区剩余大小),并不会影响响应数据的传输。
- ✨捎带应答(Piggyback Acknowledgment) 是一种优化数据传输效率的技术。它的核心思想是将确认应答(ACK)与发送方的数据包合并在一起,从而减少网络中的独立 ACK 数据包,节省网络带宽并提高效率。
在正常情况下,TCP 数据传输有两个方向:一方发送数据,另一方单独发送 ACK 确认数据包。而捎带应答的优化方式是:如果接收方即将向发送方发送自己的数据,它会将 ACK 信息嵌入到即将发送的数据包中,捎带一同发回,而不是单独发送 ACK 数据包。
💎引入捎带应答可以减少网络负载:捎带应答将 ACK 与数据合并,减少独立 ACK 数据包的数量,降低了网络中的数据包总数。提高传输效率:每个数据包都有头部信息(如 TCP 包的首部通常为 20 字节),独立的 ACK 只承载确认信息。捎带应答避免了不必要的独立数据包。节省计算资源:发送和处理每个数据包都需要消耗计算资源。通过合并,节省了计算时间和系统资源。
💎此外,捎带应答也可以减少 ACK 数据包:将 ACK 和数据包合并,降低了网络拥堵的可能性。提升数据吞吐量:数据包数量减少,网络资源可以用于传输更多的有效数据。节省头部开销:独立的 ACK 数据包需要 TCP/IP 首部,而捎带应答则复用了已经存在的数据包头部。
❗但是,捎带应答也有一定的限制和风险:需要等待合适的时机:如果接收方短时间内没有数据需要发送,捎带应答可能延迟 ACK 的发送,影响实时性。数据流单向时无效:
如果数据仅从一方向另一方传输(如文件下载),接收方没有数据需要发送,无法使用捎带应答。增加实现复杂度:捎带应答需要协调数据发送与 ACK 的时机,增加了实现的复杂性。- 捎带应答通常应用于:双向通信:比如聊天软件或双向数据传输时,双方持续有数据交互,捎带应答可以大幅减少独立 ACK 数据包。短时间高频数据传输:当双方在短时间内传递数据且有应答需求时,捎带应答能够显著提升效率。
特性
捎带应答
延时应答
原理
将 ACK 嵌入到发送方数据包中一起发送
延迟发送 ACK 数据包,等待后续数据到达再发送
适用场景
双向通信,双方都有数据需要发送
单向通信或有可能产生新数据的情况
对实时性的影响
基本不会影响,ACK 捎带在数据包中
可能增加数据传输延迟
资源效率
减少了独立 ACK 数据包的数量和头部开销
减少了 ACK 数据包的数量,但带来了额外延迟
粘包问题
- ✨粘包问题 是指在使用 TCP 协议 进行数据传输时,接收端读取数据时可能会将多个数据包粘在一起,或者一个数据包被拆分为多次读取。这个问题的本质是 TCP 是面向字节流的协议,而不是面向消息的协议。而 UDP 协议 是面向数据包传输的,便不会有这个问题。
- 由于面向字节流传输,包与包之间的界限难以区分,容易发生混淆,导致接收方无法得知哪里到哪里是一个完整的应用层数据包。
粘包的原因
TCP 是一种流式传输协议,在传输过程中,它会根据网络的情况动态调整分片大小、传输顺序等。导致粘包的主要原因有以下几点:
发送端问题:应用层将多段数据迅速发送到 TCP 缓冲区,而 TCP 协议会将这些数据拼接成一个连续的字节流发送,造成粘包。
接收端问题:接收端从 TCP 缓冲区读取数据时没有按照消息的边界读取,而是读取了多个消息内容,导致粘包。
Nagle 算法:为了提高网络传输效率,Nagle 算法会将多个小的数据包合并成一个较大的数据包再发送,可能导致粘包。
TCP 的分片机制:数据过大时,TCP 会将其拆分为多个数据包。接收端读取不及时或读取边界不明确时,会导致拆分问题。
举个例子:假设发送端按以下顺序发送三段数据:
- 数据1:
[ Hello ]
、- 数据2:
[ World ]
、- 数据3:
[ TCP ]
。在接收端可能会出现以下情况:
- 正常分包:
[ Hello ]
,[ World ]
,[ TCP ];
- 粘包:
[ HelloWorld ]
,[ TCP ];
- 拆包:
[ Hel ]
,[ loWo ]
,[ rldTC ]
,[ P ]。
解决方法
- 为了解决粘包问题,在传输层上是无解的,需要在 应用层 对数据进行处理,明确消息的边界。
固定长度协议
- 每条消息的长度是固定的,比如消息长度固定为 100 字节。
- 优点:简单明了,容易实现。
- 缺点:浪费带宽,无法灵活处理长度变化的消息。
在消息前增加长度字段
- 在每条消息的开头添加一个固定长度的字段,用来表示消息的总长度。
- 接收端先读取长度字段,再根据长度读取完整消息。
- 例如:
[6Hello]
,[5World]
使用特殊分隔符
- 在每条消息的结尾添加一个特殊字符(如
\n
或\r\n
)作为分隔符。- 接收端可以通过检测分隔符来分离每条消息。
- 例如:
Hello\nWorld\nTCP\n
- 缺点:如果消息内容中包含分隔符,需要进行转义处理。
序列化协议
- 使用如 Google Protobuf、JSON、XML 等序列化方式编码数据。它们自带消息的结构定义和边界信息,能够避免粘包问题。
- 优点:灵活、易扩展,适合复杂数据结构。
异常处理
- ✨数据在网络传输过程中是不确定的,包括传输双方的情况也是不确定的,对于这些异常情况的处理,是保证可靠传输的一大关键要点。TCP 协议 通过一系列机制应对这些问题,确保通信的正确性和完整性。
可能出现的几种特殊情况
某个进程崩溃了:
- 进程崩溃和主动断开连接并无区别,进程崩溃好比进程释放,释放文件描述符表,同时调用 close() 方法,触发四次挥手,虽然进程退出了,但是主机还在,对端保存的信息也还在,可以正常触发四次挥手。
主机正常关机:
- 正常流程的关机都不是立即关机的,而是先释放此时运行的所有的进程,释放进程其实就是上面进程崩溃的流程一样。但是也可能出现特殊情况,四次挥手可能没有挥完,主机就彻底关机了。
- 关机的一方通常是主动断开连接的,在发送 FIN 后,被动断开连接的一方会发送 ACK 和 FIN,若关机的这一方在发送完 FIN 后就彻底关机了,后续就收不到 ACK 和 FIN 了,所以被动方就无法收到 ACK 了,此时被动方触发超时重传,重传到一定次数后还是没有 ACK 的接收,就认为对端网络出现异常,此时主动放弃连接。
接收方主机掉电:
- 接收方主机掉电表现为发送方一直接收不到 ACK,对于发送方来说,就会触发超时重传,重传达到一定次数后,还是没有 ACK。
- 发送方就会发送一个 RST 复位报文(TCP 首部协议报文格式的那六个重要区域之一),表示重置 TCP 连接,重头开始再试试,此时依然没有 ACK,那么发送方便会主动放弃连接。
发送方主机掉电:
- 发送方主机掉电表现为接收方还觉得与发送方建立连接时,但是久久没有收到发送方发送来的数据,在接收方等待一定时间仍然没有接收数据后。
- 接收方发送一个 "心跳包","心跳包" 不携带任何业务数据,只是为了触发发送方的 ACK,如果对方发送了 ACK,表明对方还有 "心跳",接收方便继续等待。如果对方仍然没有发送 ACK,那么接收方发送 RST,此时依然没有 ACK,接收方便主动放弃连接。
异常情况分类
数据传输中的异常
- 数据丢失:数据包在传输过程中丢失。
- 数据重复:接收方收到相同的数据包多次。
- 数据乱序:数据包到达的顺序与发送顺序不同。
- 数据损坏:数据包内容发生变化(如被干扰)。
连接中的异常
- 连接建立失败:三次握手未完成。
- 连接断开:一方主动或被动关闭连接。
- 一方设备掉线或宕机。
- 网络阻塞导致连接不可用。
网络异常
- 高延迟:网络传输速度过慢。
- 拥塞:数据包在网络中排队等待。
- 丢包:网络丢弃部分数据包。
TCP的异常处理机制
(1) 数据传输层面
超时重传机制
- 机制: 如果发送方在指定时间内未收到确认应答(ACK),则重新发送数据。
- 处理丢包: 当数据丢失或接收方未能及时处理时,发送方会重传数据。
- 优化: TCP使用动态的重传超时(RTO)算法,根据网络状况调整超时时间,避免频繁重传。
序列号与确认应答
- 机制: 每个数据包都有唯一的序列号,接收方通过确认应答(ACK)告知发送方哪些数据已成功接收。
- 处理重复和乱序:如果接收方发现序列号重复,说明是重复的数据包,会丢弃。如果发现数据包乱序,会缓存乱序数据,等待缺失的数据包到达后再重新排序。
校验和
- 机制: TCP头部和数据部分都有校验和,用于检测数据是否被篡改或损坏。
- 处理数据损坏: 如果接收方发现校验和不一致,会丢弃数据,并等待超时重传机制生效。
(2) 连接层面
三次握手失败
- 如果在建立连接的三次握手过程中,发送方未收到对 SYN 或 ACK 的响应,则超时重传 SYN 数据包。
- 如果多次重传后仍未成功,发送方会放弃连接,并通知上层应用连接失败。
四次挥手中的异常
- 如果一方断开连接后,未收到另一方的确认,发送方会重发 FIN 数据包,直到收到确认或超时。
设备掉线或宕机
- 心跳机制: 部分长连接应用(如 WebSocket)会通过心跳包检测对方是否在线。如果多次心跳无响应,视为对方掉线。
- RST 报文: 如果设备突然掉线,操作系统可能发送 RST(复位)报文通知对方连接异常。
半开连接
- 定义: 一方正常运行,但另一方已经掉线。
- 解决: TCP通过超时机制检测到半开连接后,主动关闭连接。
(3) 网络层面
流量控制
- 机制: TCP通过滑动窗口(窗口大小由接收方决定)控制发送方的数据速率,防止发送方过快发送导致接收方处理不过来。
- 应对: 当接收方的处理能力不足时,可以将窗口大小设置为0,通知发送方暂停发送。
拥塞控制
- 机制: TCP通过慢启动、拥塞避免、快重传和快恢复等算法减少网络拥塞对数据传输的影响。
- 应对: 在检测到网络拥塞(如数据包丢失或延迟增加)时,发送方会减少发送速率并逐步恢复。
具体异常场景及处理
(1) 接收方突然掉电
- 问题: 发送方无法收到接收方的 ACK。
- 处理:发送方会尝试超时重传数据。如果多次重传后仍未收到 ACK,发送方会关闭连接。
(2) 网络短暂中断
- 问题: 数据传输中断,可能丢失部分数据包。
- 处理:超时重传丢失的数据。滑动窗口调整发送速率。拥塞控制减少网络负担。
(3) 网络严重拥塞
- 问题: 数据包丢失率增加,延迟变高。
- 处理:慢启动:降低发送速率。拥塞避免:根据网络状况逐步增加发送速率。快重传和快恢复:快速检测和恢复数据丢失情况。
(4) 一方主动关闭连接
- 问题: 四次挥手过程中,ACK 或 FIN 丢失。
- 处理: 重传丢失的 FIN 或 ACK,直到确认对方已收到。
(5) 中间人攻击
- 问题: 数据在传输过程中被篡改或劫持。
- 处理:使用 TLS/SSL 协议加密数据,防止明文被篡改。校验和检测数据是否被篡改。
URG和PSH
- 至此,TCP 协议 的所有机制就已经差不多了解完了,我们再回看 TCP 首部协议报文格式,就只剩下 16位紧急指针 以及六个字段的 URG 和 PSH 了
- URG 表示紧急指针位,TCP 正常是通过序列号和确认号来保证数据的有序传输和读取的,但可能存在特殊情况需要某些数据 "插队" 读取,而 URG 就起到了这个作用,通过将 URG 置为1 以及 填写 16位紧急指针 字段,来表示 跳过前面的数据,先从该报文开始读取。
- PSH 表示催促标志位,将 PSH 位置为1,接收方就会尽快的将这个数据包从缓冲区读取到应用程序中。
- 至于 选项 字段,再 RFC标准文档 有更多的详细说明,感兴趣的小伙伴可以详细看看,拓宽自己的知识~~~
- TCP协议的讲解到这就差不多了,下节我们看看 TCP 和 UDP 这两大传输层协议在不同场景的应用以及区别等~~~
- 毕竟不知后事如何,且听下回分解
- ❤️❤️❤️❤️❤️❤️❤️