0%

TCP/IP 协议栈系列(十二):UDP 协议详解


UDP 协议简介

UDP(User Datagram Protocol)传输与IP传输非常类似。你可以将UDP协议看作IP协议暴露在传输层的一个接口。UDP协议同样以数据包(datagram)的方式传输,它的传输方式也是”Best Effort”的,所以UDP协议也是不可靠的(unreliable)。那么,我们为什么不直接使用IP协议而要额外增加一个UDP协议呢? 一个重要的原因是IP协议中并没有端口(port)的概念。

UDP协议非常简单,但它的产生晚于更加复杂的TCP协议。早期的网络开发者开发出IP协议和TCP协议分别位于网络层和传输层,所有的通信都要先经过TCP封装,再经过IP封装(应用层->TCP->IP)。

UDP 报文格式

UDP的数据包同样分为头部(header)和数据(payload)两部分。

![udp-pakage]!(/images/udp-pakage.png)

源端口:发送方的端口号;2个字节
目的端口:接收方的端口号;2个字节
数据报长度:长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分;2个字节
校验值:校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。2 个字节

总共头部占 8个字节

UDP 协议特点

UDP使用具有最小协议机制的简单无连接通信模型。UDP提供数据完整性的校验和,以及用于在数据报的源和目标寻址不同函数的端口号。它没有握手对话,因此将用户的程序暴露在底层网络的任何不可靠的方面。如果在网络接口级别需要纠错功能,应用程序可以使用为此目的设计的传输控制协议(TCP)。

  • UDP无连接:UCP不维护连接状态,也不跟踪这些参数,开销小
  • 分组首部开销小:TCP首部20字节,UDP首部8字节。
  • UDP没有拥塞控制:应用层能够更好的控制要发送的数据和发送时间,网络中的拥塞控制也不会影响主机的发送速率
  • UDP提供尽最大努力的交付,不保证可靠交付:
  • UDP是面向报文的:添加首部后直接乡下交付为IP层,既不合并,也不拆分,保留这些报文的边界。对IP层交上来UDP用户数据报,在去除首部后就原封不动地交付给上层应用进程,报文不可分割,是UDP数据报处理的最小单位。
  • UDP 支持一对一、一对多、多对一和多对多的交互通信。

UDP校验和的计算方法和IP数据报首部校验和的计算方法相似,都使用二进制反码运算求和再取反,但不同的是:IP数据报的校验和之检验IP数据报和首部,但UDP的校验和是把首部和数据部分一起校验。

UDP 适用场景

UDP常用一次性传输比较少量数据且对数据丢失不敏感的网络应用。例如:DNS,SNMP等,因为对于这些应用,若是采用TCP,为连接的创建,维护和拆除带来不小的开销。UDP也常用于多媒体应用(如IP电话,实时视频会议,流媒体等)数据的可靠传输对他们而言并不重要,TCP的拥塞控制会使他们有较大的延迟,也是不可容忍的。

UDP 连接

客户端:直接绑定端口就可以发送数据了

1
2
3
4
5
6
7
8
9
10
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('127.0.0.1', 9999))
print('Bind UDP on 9999...')
while True:
# 接收数据:
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
# 返回数据给客户端
s.sendto(b'Hello, %s!' % data, addr)

服务端:使用UDP时,创建基于UDP的 Socket,然后,不需要调用connect(),直接通过sendto()给服务器发数据:

1
2
3
4
5
6
7
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()

参考资料

UDP协议总结