以往系列文章请参考:
网络流媒体协议之——MPEG-DASH协议简述
网络流媒体协议之——HLS概述
本篇是关于最简单的UDP协议的概述。
网络上的媒体传输一般可认为有两种方式,基于TCP的传输和基于UDP的传输,根据应用场景的不同,所选择的传输方式也不同。常用的应用层流媒体协议有RTP/RTCP/RTSP(可基于TCP或UDP)、RTMP和HTTP(一般基于TCP方式)。
对于实时视频流媒体来说,人们期望音视频数据能够及时地从一端传输到另一端,例如数字电视行业的电视直播等,少量的丢包对于观众的观看影响不明显,但较大的延迟和卡顿则严重影响观看体验。TCP的拥塞控制技术不太适用于这种快速而稳定的流媒体传输,并且重传机制对于实时流媒体来说既影响效率又没必要,因此,这种情况下一般采用基于UDP的方式。
下面我们就来近距离了解一下常用的UDP协议。
UDP — User Datagram Protocol,用户数据报协议,是OSI模型中的一种面向无连接的传输层协议。UDP在TCP/IP协议族中的位置如下图所示。
UDP提供了无连接通信,且不对传送的数据包进行可靠性保证,具有延迟小、数据传输效率高的优点,因此较多地应用于音视频的实时传输。数字电视行业的转码器,如envivo、思科等,基本都是基于UDP传输TS封装的码流。
下面我们来看看UDP包的内部结构是什么样子?
很简单,就是在有效数据前面加上一个8字节的UDP报头,UDP的报头由4个域组成,每个域各占用2个字节,分别是源端口号、目标端口号、数据报长度以及校验和。
源端口号:在源主机上运行进程所使用的端口,16 bits,可以表示的端口号范围是0~65535。当源主机是客户端时,此端口号为临时端口号(1024~5000),为源主机上的UDP软件随机生成;当源主机是服务端时,此端口号通常是熟知端口(0~1023)。
目的端口号:在目的主机上运行的进程使用的端口号,16 bits。若目的主机是服务器端,那么此端口号通常是熟知端口;如果目的主机是客户端,那么此端口号通常是随机生成的临时端口。服务器端发送报文的目的端口,通常是将客户端发送报文的源端口复制过来。
总长度:16 bits,范围0~65535,定义了用户数据报的总长度,包含报头和数据部分,但注意最小长度是8字节,即只有报头没有数据。
校验和:该字段用来检验整个用户数据报(首部+数据)出现的差错,可选,若无,则该域全为0。
在计算UDP校验和的过程中用到了一个伪首部,长度为12 bytes。伪首部的组成见下图。
伪首部包含5个部分,分别是源IP地址(4 bytes),目的IP地址(4 bytes),8 bits的全零填充字段,所使用的协议类型代码(UDP为17),以及UDP数据报的长度(2 bytes)。伪首部并不随着UDP数据报一起传输,也不计算在数据报长度之内,只用来做发送和接收端的UDP校验和的计算。使用伪首部的目的是检验UDP数据报是否已到达正确的目的地。正确的目的地包括了特定的主机地址和机器上特定的协议端口,而UDP报头仅指定了使用的协议端口号。因此为了确保数据报能够正确到达目的地,发送UDP数据报的机器在计算校验和时把目的主机的IP地址和应有的数据都包含在内。接收方进行正确性验证的时候,必须把这些字段的信息从IP报文的首部中抽取出来,以伪首部的格式进行组合,然后再重新计算校验和(如下图所示)。如果校验正确,说明UDP数据报到达了正确主机的正确端口。
有了以上这些包结构知识,下面我们来看一下UDP在形成和传输流程中的组包和解包过程,以一图以概括之:
Frame header为数据链路层的帧头。
最后,我们来简单聊一下UDP使用中的问题和常用处理方法。
UDP虽然实现简单、实时性好,但也存在丢包和乱序问题,在局域网中丢包不明显,在公网环境下丢包则较为普遍。实际使用中,针对丢包和乱序产生的原因,我们可以采取一些补救措施来改善UDP传送流媒体数据的效率。
关于丢包,主要包括以下两个方面的原因:
1. 在发送端,发送数据包过大或者太频繁导致的丢包
以太网的MTU(Maximum Transmission Unit)通常是1500 bytes,因此,最好控制发送报文长度在1500 bytes以下,以TS over UDP场景为例,通常每个TS包大小为188 bytes,因此,每个UDP包中最大可包含TS包的数目为floor((1500 – 8 -20 )/ 188) = 7,即有效数据为188 * 7 = 1316bytes,8和20分别为UDP包头和IP包头的长度。
若数据填充至UDP发送缓冲区的速度比UDP发送速度快,那么当缓冲区填满之后就会发生溢出而导致数据丢失,因此需要平衡写入缓冲区的速度与UDP发送速度。
2. 在接收端,处理速度太慢,来不及接收导致丢包
接收端的接收模块和数据处理模块应设计成并行模式(异步模式),共同维护一个接收缓冲队列,使接收和处理能够同步进行,同时保证数据处理速度不低于数据接收速度,以免buffer溢出。