在网络传输中,随机丢包是一种常见且不可避免的现象,常见的随机丢包原因有:
1️⃣网络拥塞:当网络拥塞时,网络设备(如路由器、交换机等)会出现缓存溢出、队列满等情况,导致数据包无法及时处理,从而出现丢包现象。
2️⃣传输错误:数据包可能会因为传输介质的问题或者传输过程中的干扰等原因导致数据包损坏,无法通过网络数据正确性校验,数据包被丢弃导致丢包现象。
此外,数据包的乱序也是网络传输中的常见现象,当网络拥塞时,不同数据包在传输过程中可能会经过不同路径,而不同路径的带宽和延迟不同导致数据包乱序到达。
为解决随机丢包与乱序的问题,各个传输控制协议分别引入了各自的错误恢复机制,比较典型的是:TCP引入了ACK,UDP引入了NACK,下面我们对ACK和NACK进行分析比较。
Part 01
ACK实现原理
ACK是一种正向反馈,接收方收到数据后回复消息告知发送方数据包已收到。ACK要求TCP包头中包含一个唯一ID(SeqNum),接收端收到数据包后发送“确认当前SeqNum已收到”的数据包给发送端,发送端收到确认包即认为数据发送成功。
常见的ACK实现如下👇
1.1 停等协议
发送方A发送数据, 每发送一个数据包就停止发送,开启定时器并等待接收方B发送确认, 收到确认后A关闭定时器,发送下一个数据包。若超过定时器设置的超时时间,则数据包发送失败,重新发送数据包并重启定时器。
图1 停等协议数据交互示意图
停等协议一次只能发送一个数据包,保证了准确性但牺牲了效率,对带宽的利用率也不高。
1.2 快速重传&滑窗协议
使用 ACK 机制的传输协议,通常在发送端等到某个数据包的 ACK 超时后,才会重传数据包,不够及时。快速重传的实现是如果接收端接收到了序号跳跃的数据包,则立即给发送方发送最后一个连续的数据包的 ACK(重复确认) 。如果发送端收到连续 3 个重复确认,则认为该 ACK 的下一个数据包丢失了,并立即重传该丢失的数据包。
图2 快速重传&滑窗协议数据交互示意图
触发快速重传之后,重传的方案有以下两种,具体采取哪种方案依赖于具体实现:
a.仅重传包M1,在较少丢包的时候该方案比较适用,但是如果是连续丢包场景,会不断的触发快速重传,性能反而较差。
b.从M1开始重传所有包,适用于连续丢包场景,但是较少丢包或乱序时,M1之后已被接收的包也会被重发,浪费网络资源。
快速重传协议保留了超时时间机制,超时后数据包重发,引入快速重传机制可以更快的发现数据包丢失,在未到达超时时间时便可提前重发数据并重启定时器。
1.3 连续ARQ协议&滑窗协议
发送方维持着一个一定大小的发送窗口,位于发送窗口内的所有包可以连续发送出去,中途不需要依次等待对方的ACK确认。
接收方通常采用积累确认模式,即不必对每一个包逐个发送ACK,而是在连续收到N个包后,对顺序到达的最后一个包序号发送ACK,表示这个包及之前的所有包都已正确收到了,其中N会根据网络状况和协议设计而有所不同。
图3 连续ARQ协议&滑窗协议数据交互示意图
连续ARQ协议保留了超时时间机制,超时后数据包重发。
连续ARQ协议中,在收到确认包(M4)后,之前的所有包(M1、M2、M3)也被确认。
与快速重传协议相比,连续ARQ协议减少了确认包的数目,节省了带宽。但连续ARQ协议在确认到丢包(M7)之后,处于丢包(M7)之后已被接收的包(M8)也会被重发,浪费网络资源,降低网络响应速度。
1.4 SACK协议&滑窗协议
SACK协议是在连续ARQ协议上的优化,通过在确认包头中增加已经接收到并缓存的不连续的报文段,避免丢包之后已被接收的包(图3中M8)也会被重发,从而节省带宽,加快网络响应速度。
图4 SACK协议&滑窗协议数据交互示意图
需要注意的是,SACK并不是TCP的默认项,需要通信双方均开启SACK功能支持。
对于以上四个方案,整体性能评价为SACK协议>ARQ协议≈快速重传协议>停等协议。特别是针对乱序场景:
(1)SACK可以避免重发接收端已经接受的包;
(2)快速重传策略a也可避免重发接收端已经接受的包,但是却引入了连续丢包场景不断触发快速重传的问题;
(3)快速重传策略b、ARQ协议无法避免重发接收端已经接受的包。
快速重传和连续ARQ相比,各有其适用的场景,快速重传适用于数据传输延迟要求较高的场景,如实时视频传输;而连续ARQ适用于数据传输可靠性要求较高的场景,如文件传输。
Part 02
NACK实现原理
NACK是一种负向反馈,接收方只有在没有收到数据的时候才通知发送方。NACK要求UDP包头中包含一个唯一ID(SeqNum),接收端收到数据包后,检查SeqNum是否连续,记录缺失的SeqNum,等待定时发送NACK请求,要求发送端重发。
图5 NACK协议数据交互示意图
定时发送NACK的时间由用户自定义,一般为20ms,在一个定时发送周期内到达的乱序包不会请求重发,但不在一个定时发送周期内到达的乱序包会冗余重发。
Part 03
ACK与NACK性能对比
由于SACK性能在ACK中最佳,因此我们只比较NACK与SACK。
- SACK的确认包丢失可能会导致数据包发送超时,重发接收端已接收的数据包;NACK反馈包丢失,下一个反馈包会携带上一个反馈包的信息。NACK避免了已接收数据包的重发,但因为缺少超时机制,发送端丢包重发完全依赖于NACK反馈包,重传灵敏度略低于SACK。
- 受限于TCP滑动窗口的大小(100-200个),SACK必须等待滑动窗口中的数据全部发送才能向后继续发送新的数据包,这会引入部分时延;NACK历史数据队列完全由用户控制,无此限制(一般为1000个或2s内数据)。
- 受限于TCP头的大小,SACK一个确认包中只能携带3组提前收到确认数据,在强丢包场景下性能退化严重,很容易导致冗余重发;NACK在强丢包场景下性能略微退化,会导致部分冗余重发,但优于SACK。
综上,在网络数据较好时,NACK与SACK各有优劣;在强丢包环境中,NACK性能强于SACK。但个人认为在强丢包环境中,SACK策略与NACK策略仍是各有优劣,但SACK受限于TCP的框架,导致性能不如NACK。
总的来说,基于TCP的SACK适用于效率要求较低、但准确性要求较高的场景,例如文件传输、接收邮件、远程登录;基于UDP的NACK适用于效率要求较高,但准确性要求不高的场景,例如实时音视频、快直播、家庭教育、在线视频观看等类直播场景。