1. 概览
1.1 UDP协议
UDP是工作在OSI(开放系统互连,Open Systems Interconnection)模型中传输层的协议。它使用IP作为底层协议,是为应用程序提供一种以最少的协议机制向其他程序发送消息的协议。其主要特点是无连接,不保证可靠传输和面向报文。RFC768为IETF(互联网工程部,Internet Engineering Task Force)提供的UDP标准。
UDP 与 TCP 处于 OSI 模型 的传输层,都属于传输层协议。
1.2 总览
项目 | UDP |
---|---|
面向连接 | 不面向连接 |
可靠性 | 不可靠连接,无流量控制 、拥塞机制 |
数据传输速度 | 较快 |
首部开销 | 8字节 |
传输方式 | 面向报文 |
交互方式 | 单播(1:1)、广播(1:n)、组播(n:m)、任播 |
用途 | 游戏、语音、视频等场景 |
优点 | 低延迟、低开销、无连接特性、支持组播与广播 |
缺点 | 不可靠传输、无流量控制、无拥塞控制、安全性较低 |
无连接:发送方在发送数据之前不需要与接收方建立连接,即刻可以传输数据,每个UDP数据包都是独立的,相互之间没有关联,因此UDP可以一对一、一对多或多对多发送消息。
面向报文:发送方的UDP对应用程序交下来的报文,再添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。接收方的UDP对IP层交上来的UDP用户数据报,在去除首部后就原封不动地交付上层的应用进程。UDP一次交付完整的报文,因此应用程序必须选择合适大小的报文。
1.3 在网络层中数据结构
1.4 在GB/T 28181协议中的应用
版本 | UDP应用细化 |
---|---|
GB/T 28181 | - UDP由于其无连接、低延迟的特性,常被用于传输实时的音视频媒体流 - 信令交互 |
1.5 头部结构解析
-
源端口号(Source Port)(16位->2个字节):发送端口,范围为0-65535。有时候可能不会设置源端口号,没有源端口号的时候该字段的值设置为0。用于不需要返回的通信中。
-
目的端口号(Destinaton Port)(16位->2个字节):接收端口,范围为0-65535
-
长度(Length)(16位->2个字节):UDP头长度 + 数据长度。
通过 IPv4 协议传输时,由于IPv4的头部信息要占用20字节,因此数据长度不可能超过65507字节(65,535 − 8字节UDP头 − 20字节IP 头)。
在 IPv6 的 jumbogram 中,是有可能传输超过 65535 字节的UDP数据包的。依据RFC2675,如果这种情况发生,报文长度应被填写为0。
-
校验和(Checksum)(16位->2个字节):对整个UDP包,包括UDP头部和UDP数据的校验,使用一种简单粗暴的循环冗余校验(Cyclic Redundancy Check, CRC)算法机型校验,该字段在 IPv4中是可选的,在 IPv6中则是强制的。如果不使用校验和,该字段应被填充为全0。
为了数据安全,同TCP一样,计算校验和时还需计算伪头部,IPv4在计算检验和时,临时把伪头部和 UDP 用户数据报连接在一起。伪首部包括了IPv4头部中的一些信息,但它并不是发送IP数据包时使用的IP数据包的头部,而只是一个用来计算校验和而已。接收主机在收到UDP报文以后,从IP首部获悉IP地址信息构造UDP伪首部,再进行校验和计算。 IPv6下加入伪头部计算方式与IPv4稍有不同,主要因ip地址所占大小不同,这里就不再赘述。
识别一个通信应用需要5个因素。"源IP地址"、"目标IP地址"、"源端口"、"目标端口"、"协议号"。UDP首部只包含了(源端口和目标端口),用此来校验,如果其他三项信息被破坏,极有可能导致应收包应用收不到,不应该收包的应用收到。为此有必要在通信中,验证这5项的识别码是否正确,就引入了伪头部的概念。
1.6 示例
上图为抓取UDP包,我们使用Wireshark工具进行分析(UDP基于IP协议,如感兴趣可根据工具了解其他字段):
13 c4 | 13 c4 | 02 54 | 15 e9 | data |
---|---|---|---|---|
十进制:5060,为源端口 | 十进制:5060,为目的端口 | 十进制:596,为发送数据长度 | CRC校验位 | ... |
1.7 报文长度
1.7.1 报文大小的影响因素,主要有以下3个
- UDP协议本身,UDP协议中有16位的UDP报文长度,那么UDP报文长度不能超过2^16=65536.
- 以太网(Ethernet)数据帧的长度,数据链路层的MTU(最大传输单元)。
- socket的UDP发送缓存区大小
1.7.2 数据包最大长度
- UDP数据包最大长度: UDP头部确实占用8字节,其中包括源端口号、目的端口号、数据长度和校验和。理论上,UDP数据段的长度字段是一个16位的字段,因此最大值为2^16 = 65536字节。但由于整个IP数据包(包括IP头部)的大小限制,实际可传输的UDP数据最大长度要减去IP头部的大小(通常是20字节)和UDP头部的大小(8字节),得到65536 - 20 - 8 = 65508字节。但是,通常人们提到的UDP最大数据包大小是65507字节,这是因为需要考虑到UDP数据段的长度字段本身也占用1字节(数据长度字段存储的是UDP数据段的长度,不包括UDP头部的8字节),故实际最大应用数据长度为65535 - 8(UDP头部)= 65527字节,不过在实际应用中,通常认为最大数据载荷为65507字节,这可能是因为在一些解释中直接从UDP数据段的长度限制减去了头部和IP头部的长度,未专门区分UDP数据段长度字段的计数方式。
- 操作系统缓冲区限制: 您提到了Linux下UDP发送缓冲区大小的查看方法,这是通过
/proc/sys/net/core/wmem_default
获取的。实际上,这个值表示的是默认的UDP发送缓冲区大小,如果一个UDP数据包的大小超过了这个值,send
或sendto
函数将会返回错误,错误码可能是EMSGSIZE
(错误码100,Message too long)或者ENOBUFS
(错误码105,No buffer space available),具体取决于操作系统和库的具体实现。这表明,即使理论上UDP可以发送接近65507字节的数据包,实际操作中也受限于系统的缓冲区配置。
1.7.3 UDP数据包理想长度
理论上UDP报文最大长度是65507字节,实际上发送这么大的数据包效果最好吗?以下几点可以帮助确定一个合适的数据包长度
-
MTU(最大传输单元)考虑: 以太网的默认MTU是1500字节,包括IP头部和UDP头部在内的总数据包大小不应超过此值,否则可能会引发IP分片,导致数据包在传输过程中效率下降、延迟增加或甚至丢失。为了确保数据包不被分片,通常建议UDP数据负载保持在MTU减去IP头部(20字节)和UDP头部(8字节)以下,大约为1472字节左右。
-
网络路径和MTU发现: 实际网络路径中可能包含MTU小于标准以太网的链路,如某些无线网络或老旧网络设备。应用层可以通过路径MTU发现(Path MTU Discovery)机制动态调整数据包大小,以避免分片。
-
应用需求:
- 对于实时性要求极高的应用(如VoIP、在线游戏),小数据包可以减少延迟,但可能增加网络拥塞。
- 对于大量数据传输(如文件传输、视频流),较大的数据包能减少协议头开销比例,提高传输效率,但需注意避免分片。
-
缓冲区和内存管理: 应考虑系统和网络设备的缓冲区大小限制,过大的数据包可能导致缓冲区溢出或资源分配问题。
-
丢包率和重传策略: 较大的数据包丢失会导致更多的数据重传。根据应用的丢包容忍度和重传机制,可能需要调整数据包大小以平衡重传成本和传输效率。
2. 工作原理
2.1 数据包发送与接收流程
- 数据报发送过程:
- 数据封装: 当应用程序需要发送数据时,它首先将数据和目的地址(包括目的IP地址和端口号)传递给UDP层。UDP层会在数据前面添加UDP头部,该头部包含了源端口号、目的端口号、数据报的总长度以及一个可选的校验和。校验和用于检测数据在传输过程中是否出现错误。
- 数据发送: 一旦数据报被封装好,UDP将其交给网络层(通常是IP)。IP层负责进一步封装数据报,为其添加IP头部,包含源IP地址和目的IP地址,然后根据网络路由信息将数据包发送出去。在这个阶段,UDP并不关心数据包是否会到达目的地,也不确认对方是否已准备好接收数据。
- 数据接收: 接收端的网络层接收到数据包后,根据IP头部信息将数据包递交给UDP层。UDP根据UDP头部中的目的端口号将数据报交付给相应的应用程序。如果应用程序没有绑定到该端口,或者数据报在传输中损坏(校验和验证失败),UDP可能直接丢弃数据报。
- 错误处理与重传: 与TCP不同,UDP不负责数据包的排序、丢失检测或重传。如果有数据包丢失、重复或乱序,UDP不会尝试纠正这些错误。这使得UDP传输速度快,但可靠性较低。因此,使用UDP的应用程序通常需要自己实现错误检测、恢复和流量控制机制,或者能够容忍这些传输错误。
- 数据报接收过程:
- 目标主机的IP层接收到数据报后,根据IP头部信息将数据传递给UDP层。
- UDP层检查数据报的目的端口号,将数据报交付给对应端口上的应用程序。
- 如果数据报的校验和验证失败,或者没有应用程序监听该端口,UDP可以选择丢弃数据报而不通知发送方。
- 数据报拆分:先读取8字节的定长报头,在读取里面的16位UDP长度字段,减去8字节后得到的就是有效载荷的长度,知道了报头和有效载荷的大小就能把二者区分。
操作系统在收到 UDP 报文后,会将其插入到队列里,队列里的每一个元素就是一个 UDP 报文,这样当用户调用 recvfrom() 系统调用读数据的时候,就会从队列里取出一个数据,然后从内核里拷贝给用户缓冲区
2.2 传输方式
- 单播(Unicast) : 单播是最常见的数据传输方式,指每个数据包从一个源地址发送到一个明确的目标地址。在UDP中,单播传输确保数据包被直接发送给指定的接收者,这种一对一的通信方式适用于绝大多数需要可靠指向性传输的场景。
大多数互联网应用如网页浏览、电子邮件、即时消息等均采用单播传输。例如,当你在浏览器中请求一个网页时,服务器会通过单播方式将网页内容发送到你的设备上。
- 广播(Broadcast) : 广播允许数据包被发送到同一网络广播域内的所有主机。在UDP中,当数据包的目的IP地址被设置为广播地址时,网络中的所有主机都会接收到该数据包。广播常用于局域网内的服务发现、通知或系统消息的传递,但因其对网络资源的潜在消耗,其使用在很多网络中受到了限制。
局域网中的设备发现(如DHCP请求、网络打印机查找)常常使用广播。另外,某些紧急警报系统和本地网络的系统通知也会采用广播方式来确保信息迅速传达给所有连接的设备。
- 组播(Multicast) : 组播是一种一对多的高效数据传输方式,它允许数据包发送到一组特定的主机,这些主机表达了对特定组播组的兴趣。与广播不同,组播数据包只被送到明确请求接收的主机,因此可以有效地利用网络资源,特别适用于需要向多个接收者同时发送数据的场景,如在线视频会议、直播、股票行情推送等。
在线视频直播、网络电视(IPTV)、视频会议系统等广泛应用组播技术,以高效地向大量用户同时传输相同的数据。例如,体育赛事直播可以利用组播,让成千上万的观众同时流畅观看,而不需要为每个观众单独发送数据流。
- 任播(Anycast) : 虽然不是UDP直接提供的数据传输方式,但在网络层(如IPv6)中,任播提供了一种寻址机制,使得一个数据包可以发送到一组共享相同任播地址的设备中的任意一个。路由器会根据网络拓扑和距离等因素,选择最优的路径将数据包转发给最近或最合适的接收者。任播在内容分发网络(CDN)和分布式服务中有所应用,但其行为和效果更依赖于网络基础设施而非UDP协议本身。
大型的互联网服务提供商可能会使用Anycast来部署全球的DNS服务器,使得用户请求可以路由到离他们最近或响应最快的服务器上,从而提高了服务的可用性和响应速度。
2.3 丢包、重复包与乱序包的问题及处理方法
2.3.1 丢包问题及处理方法
原因:
- 网络拥塞:当网络中的数据量超过其承载能力时,路由器可能丢弃部分UDP数据包。
- 网络故障:如线路中断、设备重启等,都可能导致数据包丢失。
- 错误检测:虽然UDP提供了校验和来检测数据错误,但检测到错误时,UDP并不尝试重传,而是直接丢弃。
处理方法:
- 应用层重传:应用程序可以实现自己的重传机制,例如发送端维护发送序列号,接收端收到后回传确认,未收到确认的数据包则重传。
- 前向纠错码(FEC) :发送端在数据包中加入冗余信息,即使部分数据丢失,接收端也能通过冗余信息恢复原始数据。
- 流量控制:合理控制发送速率,避免网络拥塞导致丢包。
2.3.2 重复包问题及处理方法
原因:
- 网络延迟和重传机制可能导致同一数据包被多次发送并最终到达接收端。
- 网络路由异常也可能造成数据包复制。
处理方法:
- 序列号:在数据包中加入序列号,接收端通过比较序列号判断是否为重复包,并丢弃重复数据。
- 应用层去重:接收端维护一个接收窗口,记录已接收的数据包序列号,避免处理重复数据。
2.3.3 乱序包问题及处理方法
原因:
- 不同数据包在网络中可能经过不同的路径,路径长度不一导致到达时间不一致。
- 网络拥塞或路由器处理速度不均也可能造成数据包顺序错乱。
处理方法:
- 序列号和时间戳:除了序列号,还可以加入时间戳,帮助接收端按照正确的顺序重组数据包。
- 缓存与排序:接收端缓存接收到的数据包,直到所有预期的数据包都到达后再按照正确的顺序处理。
- 滑动窗口协议:在某些应用中,可以使用类似于TCP的滑动窗口协议来管理数据包的接收和排序,但这种方式会增加实现复杂度。
2.4 系统中使用UDP协议的常用的程序使用特定端口号
- DNS(Domain Name System) : 端口53
- 用于域名解析服务,将人类可读的域名转换为IP地址。
- NTP(Network Time Protocol) : 端口123
- 用于时间同步服务,确保网络中各个系统的时间一致。
- SNMP(Simple Network Management Protocol) : 端口161
- 用于网络设备管理和监控。
- TFTP(Trivial File Transfer Protocol) : 端口69
- 一种简单的文件传输协议,常用于网络设备配置文件的上传下载。
- DHCP(Dynamic Host Configuration Protocol) : 客户端使用端口68,服务器使用端口67
- 自动分配IP地址和其他网络配置信息给网络中的设备。
- BOOTP(Bootstrap Protocol) : 客户端使用端口68,服务器使用端口67
- 类似于DHCP,用于早期的网络设备自动配置。
- SSDP(Simple Service Discovery Protocol) : 多个端口,包括1900
- 用于发现网络上的UPnP(Universal Plug and Play)设备。
- Syslog: 端口514
- 用于系统日志消息的收集和传输。
4. 安全性
UDP协议本身并未内置强大的安全性措施,这使得在使用UDP传输数据时,存在一系列安全考量。以下是几个主要的安全性考量点及其可能的解决方案:
-
数据完整性与校验和:
- UDP头部包含一个可选的校验和字段,用于检测数据报在传输过程中是否发生了错误。然而,这个校验和只覆盖UDP头部和数据部分,且校验范围有限,可能无法检测到所有类型的错误。为了提高数据完整性,应用层可以实施额外的校验和或哈希机制。
-
易受攻击性:
- 由于UDP是无连接的,缺乏身份验证和加密,因此容易受到各种攻击,如IP欺骗、UDP泛洪攻击(DDoS的一种形式)和中间人攻击。
- 解决方案:使用上层应用协议提供认证机制,或者采用如DTLS(Datagram Transport Layer Security)等协议为UDP数据提供加密和身份验证。
-
隐私保护:
- UDP数据报文以明文形式传输,任何截取到数据的人都可以读取内容,这对于传输敏感信息是不安全的。
- 解决方案:在应用层实施端到端的加密,如使用SRTP(Secure Real-time Transport Protocol)对VoIP通信进行加密,或直接使用如TLS over UDP(如QUIC协议)。
-
流量分析:
- 尽管UDP数据内容可能被加密,但攻击者仍可以通过分析流量模式(如包大小、发送频率)来获取信息。
- 解决方案:实施流量混淆技术,如使用恒定大小的数据包、随机的发送间隔等,以降低流量分析的风险。
-
端口扫描与服务探测:
- 开放的UDP端口容易受到扫描,攻击者可以借此发现可利用的服务。
- 解决方案:实施严格的防火墙规则,仅允许必要的端口对外开放,并对UDP流量进行监控和过滤。
-
多播和广播的安全性:
- UDP支持多播和广播,这可能引入额外的安全风险,如未经授权的接收者加入多播组或广播数据被恶意监听。
- 解决方案:使用IGMPv3(Internet Group Management Protocol version 3)等协议,实现更细粒度的多播成员控制,并在应用层实现权限验证。
5. UDP调优策略
-
调整UDP缓冲区大小:
- 增大发送和接收缓冲区的大小可以减少因缓冲区满而导致的数据包丢失。在Linux系统中,可以通过修改
/proc/sys/net/core/wmem_max
和/proc/sys/net/core/rmem_max
来增大缓冲区大小。 - 注意,缓冲区大小的调整应当依据实际网络状况和应用需求,过大可能会占用过多系统资源。
- 增大发送和接收缓冲区的大小可以减少因缓冲区满而导致的数据包丢失。在Linux系统中,可以通过修改
-
优化TCP/IP堆栈参数:
- 调整网络接口的MTU(最大传输单元)以减少分片,通常推荐值为1500字节,但对于某些链路,可能需要手动设置更低的值来避免分片。
- 开启或调整TCP/IP堆栈中的拥塞控制算法,虽然直接针对TCP,但整体网络状况的改善也会影响UDP传输。
-
使用UDP加速技术:
- 考虑使用QUIC等协议,它们在UDP之上实现了更好的拥塞控制、流量控制和加密,提升了UDP的性能和安全性。
-
减少数据包的开销:
- 减少每次发送的数据包数量,通过合并小数据包来减少协议头部的开销。
- 如果应用允许,可以使用UDP分片来组织数据,但需谨慎以避免网络层的额外分片。
-
实施应用层的优化:
- 实现应用层的流量控制和拥塞避免,如自适应发送速率控制,根据网络反馈动态调整发送速率。
- 添加重传和错误恢复机制,虽然增加了复杂度,但可以提高数据传输的可靠性。
-
监控与诊断:
- 使用netstat、iptraf、wireshark等工具监控网络性能,分析丢包、延迟等问题。
- 定期检查网络硬件和软件配置,确保它们支持高性能UDP传输。
-
多路径和多连接:
- 在条件允许的情况下,利用多路径传输(如MPTCP、Multipath UDP)分散流量,减少单点故障的影响。
- 对于某些实时应用,可以在应用层实现多连接冗余,以提高可用性和容错能力。
6. UDP其他相关技术
6.1 打洞技术
由于NAT设备会将内部私有IP地址和端口转换为公共IP地址和端口,直接从一个NAT后的设备连接到另一个NAT后的设备变得复杂。UDP打洞技术通过以下步骤绕过这一障碍:
- 中继服务器:两台客户端都需要连接到一个公共的中继服务器。这个服务器的作用是帮助双方交换必要的网络信息,而不直接参与后续的数据传输。
- 信息交换:每个客户端通过UDP向中继服务器发送数据包,中继服务器记录下每个客户端的公共IP地址和端口号,并将这些信息转发给另一方。这样,每个客户端都知道了对方的预测公共地址。
- 尝试直接连接:有了对方的公共地址信息后,每个客户端开始尝试直接向对方的预测地址发送UDP数据包。如果两个NAT设备都遵循了全锥型(Full Cone)、地址限制锥型(Address-Restricted Cone)或端口限制锥型(Port-Restricted Cone)的NAT类型,那么这些数据包可以穿过NAT到达对方,从而在两个客户端之间直接建立连接。
- 建立连接:一旦第一个数据包成功到达,就在两个客户端之间创建了一个“洞”,之后的数据可以直接通过这个洞传输,不再需要中继服务器。