认识网络通信中的 ACK、NACK 和 REX

认识网络通信中的 ACK、NACK 和 REXACK NACK REX 在面试或者网络通信的时候 我们可能经常听到和遇到 今天就来详细介绍一下 ACK NACK REX 认识 ACK NACK REX ACK Acknowledgem 它是一种正向反馈 接收方收到数据后回复消息告知发送方 NACK Negative

大家好,我是讯享网,很高兴认识大家。

ACK、NACK、 REX在面试或者网络通信的时候,我们可能经常听到和遇到。今天就来详细介绍一下ACK、NACK、 REX。

认识ACK、NACK、 REX

  1. ACK:Acknowledgement,它是一种正向反馈,接收方收到数据后回复消息告知发送方。
  2. NACK:Negative Acknowledgement,则是一种负向反馈,接收方只有在没有收到数据的时候才通知发送方。
  3. REX:Retransmission,重传,当发送方得知数据丢失后,重新发送一份数据。

那么ACK、NACK、 REX又能为我们解决什么问题了,下面一一介绍ACK、NACK、 REX在网络通信中担任的角色以及所能解决的问题。

使用ACK、NACK、 REX解决常见问题
1:判断接收方数据包是否已经丢失:

编号,为每一个 packet 都打上一个序列号(Seq number),接收端发现序列号跳变/缺失,则可以判断数据包丢失了。
这就是为什么 TCP 协议的包头(packet header)里面要定义一个序列号字段的原因(如图红色标记):


讯享网

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

扩展一下,我们再看看 UDP 协议的包头(packet header)的定义:

2:判断发送方数据包是否已经丢失

有几种常见的方案,这里先简单介绍如下

接收方通常采用 积累确认模式,即不必对每一个包逐个发送 ACK,而是在连续收到几个包后,对顺序到达的最后一个包序号发送 ACK,表示:这个包及之前的所有包都已正确收到了。

注意:

积累确认模式的缺点:乱序比较严重的网络下,效率非常低,部分已经送到但没有按照顺序送达的包也必须重传。改进方案:选择性重传(注:KCP/SRT 协议有实现):对于顺序的包,发送积累确认;跳跃的包,发送 ACK;发送端只重传真正丢失的数据包。

接收方通常采用 积累确认模式,即不必对每一个包逐个发送 ACK,而是在连续收到几个包后,对顺序到达的最后一个包序号发送 ACK,表示:这个包及之前的所有包都已正确收到了。

积累确认模式的缺点:乱序比较严重的网络下,效率非常低,部分已经送到但没有按照顺序送达的包也必须重传。

改进方案:选择性重传(注:KCP/SRT 协议有实现):对于顺序的包,发送积累确认;跳跃的包,发送 ACK;发送端只重传真正丢失的数据包。

3. 重传超时的计算规则?

RTO:重传超时时间(Retransmission Timeout),它是发送端用来判断数据包丢失和执行重传的最重要的一个参数。

很明显,它应该是一个随网络传输的 RTT(往返时间)而变化的值,理想情况下,RTO 的值不小于 RTT 即可(从数据包发送到对方的 ACK 到达的最短时间),实际情况下,RTT 变化是非常频繁的,每一次传输的 RTT 可能都不一样,如果粗暴地设置 RTO = RTT 则一定会导致重传过度频繁。

因此 TCP 协议采用的 RTO 计算方法是:

基于多次 RTT 测量,给出一个平滑后的 RTT 预估值:SRTT(Smoothed Round Trip Time)
RTO = SRTT + 某种系数(防止抖动的阈值)
进一步,系统级别设置 RTO 的下限为 100ms 或 200ms,防止异常值
更进一步,对于重传包的 RTO,加上一个退避算法,比如,每重传一次,则 RTO = 2 RTO 这样的方式来减少对一个包进行频繁的重传
`注意:`关于重传包 RTO 的退避策略,KCP 经过实验证明 ,RTO 的退避系数使用 1.5 倍的效果比 2 倍的效果更好。

4. 发送方的数据包要缓存多久?

发送端为了能实现重传,必须在本地将发送的数据包缓存起来,在需要重传的时候,即可从缓存队列里面取到该数据包进行重传。对于 ACK 模型的传输协议(如:TCP),在收到对方的 ACK 之后删除缓存即可,那如果是 NACK 模型的传输协议,如何更新和清理这个缓存队列呢 ?

cache time = 2 * rtt + x

假设在这个时间后内收到的 NACK 反馈包没有指出该数据包丢失,则可以删除了。当然,类似 RTO 所涉及到的问题原因,rtt 是频繁变化的,因此单纯依靠这个理论值来删除缓存并不安全,建议增加一定的冗余。

更加合理的方案是:每间隔指定的时间(比如:WebRTC 使用的是 10ms)发送一次 NACK 请求,一次性带上这段时间所有的丢包序号。

6. 哪些丢失的数据包会放入 nack 请求队列中 ?

接收端的重传请求的队列也应该有一定的机制,不是所有的丢包都必须要求重传,比如:

当前音频播放到了 timestamp 为 x 的时间点了,其实在 timestamp < x 的所有丢失的音频包都不应该再请求重传了,视频也是如此。

作为 SFU 中转服务器,它没有播放时间的概念,因此方法 1 并不适用,但是可以参考发送端缓存的逻辑,假设 GOP 是 2s,则对比最新的 packet 时间戳,丢失的数据包时间在 2s 之前的数据,则没有必要再申请重传了。

补充一点,作为 SFU 中转服务器,可能会遇到下述情况,即:收到客户端的 NACK 请求的数据包不再自己的 cached packet list 里面:

SFU 作为客户端上行的接收端,发现丢包也跟普通的接收端一样,定时主动地向源头发送 NACK 请求;反过来,SFU 作为客户端下行的发送端,收到 NACK 请求后,如果发现不在 cached list,则标记一下,一旦收到源头的重传,则第一时间转发到下行。

当一个丢失的包被 NACK 请求重传了至少 N 次(如:10次)后依然没有成功收到,则应该放弃了(很可能发送端也已经没有这个数据包的缓存了)

考虑到重传请求在发送端的响应时间及网络 RTT,接收端应该确保在一定时间周期内不要频繁地发送对同一个数据包的 NACK 重传请求。(如:WebRTC 选择的时间周期是 5 + RTT * 1.5),即:在这个时间周期内不再重复发送同一个数据包的 NACK 请求。

当 nack reqeust list 里面的数据包太多了(比如:超过 1000),则应该考虑清理一下(网络太弱了),对于视频的话,直接发送 IDR request,重新申请新的 GOP 数据。

FEC 包不需要,意义不大,FEC 的目的是为了减少重传而增加的冗余包,丢掉没有致命的影响,我们只需要重传价值更大的数据包即可。

9:RTCP 协议的 NACK 报文是如何定义的 ?
RTCP 的反馈报文包头定义如下,FMT 和 PT 决定了该报文的类型,FCI 则是该类型报文的具体负载:

协议规定的 NACK 反馈报文的 PT= 205FMT=1FCI 的格式如下(可以附带多个 FCI,通过 header 的 length 字段来标示其长度):
在这里插入图片描述

这里的设计比较巧妙,它可以一次性携带多个连续的数据包的丢包情况:

  • PID(Packet identifier),即为丢失的 RTP 数据包的序列号
  • BLP(Bitmao of Lost Packets),通过掩码的方式指出了接下来 16 个数据包的丢失情况

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

小讯
上一篇 2025-03-17 14:19
下一篇 2025-01-27 16:40

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/17629.html