ACK在TCP连接中是数据包确认消息,在TCP连接中,数据接收端在接收到一个数据包的时候会立即发送一个ACK消息给发送端,通知已经接收到此数据包,然后发送端再继续发送下一个数据包。
NACK与ACK刚好相反,在UDP通信中,数据接收端接收到数据包后是不需要通知发送端的,发送端始终不断的发送数据包而不关心对方是否正确收到,亦不关心所发生的数据包是否有序到达。只有在接收端意识到有某个或某几个数据包没有接收到的情况下才会构造一个NACK消息包发送给发送端。请求发送端重发丢失包。
比如接收端收到数据包100, 101, 103,105, 然后发现102, 104丢了,会构造一个NACK包(pid=102)发送给发送端。
在WebRtc的源码中,NACK模块是存在于VCMJitterBuffer模块中的。也就是在接收端接收到数据包,扔给JitterBuffer模块,在解析为数据帧待播放前才会确认是否有丢包,若有,则将丢失的包的序列号(Sequence number)以NACK消息的方式发送给发送端。
注意在webrtc通信中需要在SDP(媒体信息描述文件)中描述本端是否支持NACK消息处理。
以下是NACK的消息报文格式及对应数据结构
// NACK 报文格式
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| RC | PT=RR=201 | length | header
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender | //公共头
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC of media source | //NACK块
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PID | BLP |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//注意version, padding, blockcount, packettype, length , ssrc为公共头部分,即所有消息都具有
//此头信息,而ssrcsource, pid, blp为NACK消息特有部分。
class NackStructor {
public:
uint32_t version :2; //协议版本号(2bit)
uint32_t padding :1; //是否有填充(1bit)可理解为是否有其他跟随或补充信息在消息中
uint32_t fmt :5; //(5bit)在不同消息中叫法不同,也叫blockcount或RC,此处指格式
//format,在NACK消息中此值为1,之所以同时具有fmt和packettype
//是因为某些消息存在多级类型,比如当前的NACK消息属于Feedback
//类型。因此fmt=1(Feedback).
uint32_t packettype :8; //包类型(8bit),NACK消息类型值为205,Feedback子类型
uint32_t length :16; //(16bit)消息长度
uint32_t ssrc; //构造发送当前消息包的端的SSRC,若本端是此NACK包的发送者,此
//SSRC值就是本端的SSRC值(源标识符)
uint32_t ssrcsource; //NACK消息部分,也是SSRC值,在此可称为媒体源标识符,用来指明
//此消息要反鐀谁的情况。比如我是接收端,我知道发送端的音频流
//(SSRC:1234)给我发来的数据包有丢包,我要反鐀对方音频流的
//情况,那这个地方就应该填写1234(对方音频流SSRC)。
uint32_t pid:16; //packet id(sequence number)详见下面PID-BLP图
uint32_t blp:16;
};
//在一个消息的一个公用头后面的NACK块部分可以有多个,每个NACK块可以反鐀1个流(SSRC)的情况。当然
//1个NACK块中的blp部分也可以有多个。
PID-BLP图:
另:对于NACK包的解析及构造,在github上有很多开源的基于webrtc的项目可以找到,比如纯C实现的Janus, C++实现的Kurento等。也可以直接在webrtc源码中的VCMJitterBuffer模块中找到。