1980年8月,紧随TCP/IP之后,UDP(User Datagram Protocol,用户数据报协议)被加入了核心网络协议套件。UDP的主要功能和亮点并不在于它引入了什么特性,而在于它忽略的那些特性。UDP一般称为无(NULL)协议,RFC768描述了它的运作机制,全文完全可以写在一张餐巾纸上!
数据报:一个完整、独立的数据实体,携带者从源节点到目的节点的足够信息,对这些节点间之前的数据交换和传输网络没有任何依赖。
数据报(datagram)和分组(packet)经常被搞混。其实他们是有区别的:分组可以用来指代任何格式化的数据块,而数据报则通常只用来描述那些通过不可靠的服务传输的分组,既不保证送达,也不发送失败通知。正因为如此,很多场合下人们都把UDP中的User换成Unreliable。于是UDP成了“不可靠数据报协议”。
关于UDP的应用,最广为人知的就是DNS(Domain Name System,域名系统)。DNS负责把对人类友好的域名换成对应的IP地址。可是,尽管浏览器会依赖于UDP,但是这个协议以前从未被看成是网页或应用的关键传输机制。不过这都是过去的事了。IETF和W3C共同制定了一套新的API–WebRTC(Web Real-time Communication,Web实时通信)。WebRTC着眼于在浏览器中通过UDP实现原生的语音和视频实时通信。
要理解UDP为什么被称为“无协议”,需要从UDP下一层的IP协议说起。
IP的主要任务是按照地址从源主机向目标主机发送数据。为此,消息会被封装在一个IP分组里面,其中包含了源地址、目标地址,以及其他一些路由参数。注意,数据报一词表示IP是不保证消息可靠交付的,也不发送失败通知,实际上是把底层网络的不可靠性直接暴露给了上一层。如果某个路由节点因为网络拥塞、负载过高或其他原因删除了IP分组,在必要的情况下,IP的上一层协议要负责检测、恢复和重发数据。
UDP会使用自己的分组结构封装用户消息,它只增加了4个字段:源端口、目标端口、分组长度和校验和、这样,当IP把分组送达目标主机时,该主机能够拆开UDP分组,根据目标端口找到目标应用程序,然后把消息发送过去。仅此而已。
事实上,UDP数据报中的源端口和校验和字段都是可选的。IP分组的首部也有校验和,应用程序可以忽略UDP的校验和。也就是说,所有错误检测和错误纠正工作都可以委托给上层的应用程序。说到底,UDP仅仅是在IP层之上通过嵌入应用程序的源端口和目标端口,提供了一个“应用程序多路复用”机制。因此,UDP的无服务说的是:
令人遗憾的是,IPv4地址只有32位,因此最多只能提供42.9亿个IP地址。1994年为了解决IPv4即将耗尽这一问题,提出了一个临时性的方案,IP网络地址转换器(NAT,Network Address Translator)。
建议的IP重用方案就是在网络的边缘加入NAT设备,每个NAT设备负责维护一个表,表中包含本地IP地址和端口到全球唯一(外网)IP和端口的映射。这样,NAT设备背后的IP地址空间就可以在各种不同的网络中得到重用,从而解决地址耗尽的问题。然而,这样的临时方案居然就一直沿用了下来。新增的NAT设备不仅立竿见影的解决了IP地址耗尽的问题,而且迅速成为很多公司及家庭代理和路由器、安全装置、防火墙,以及其他许多硬件和软件设备的内置组件。而UDP经常用来和这些中间设备进行交互。
NAT转换的问题(至少对于UDP而言)在于需要维护一份精确的路由表才能保证数据转发。NAT设备依赖连接状态,而UDP没有状态。这种根本上的错配是很多UDP数据报传输问题的总根源。况且,客户端前面有很多NAT设备,因此问题进一步恶化。
每个TCP连接都有一个设计周密的协议状态机,从握手开始,然后传输应用数据,最后通过明确的信号确认关闭连接。在这设计下,路由设备可以监控连接状态,根据情况创建或删除路由表中的条目。而UDP没有握手,没有连接终止。
发送出站UDP不费事,但路由响应却需要转换表中的一个条目来告诉我们本地目标主机的IP地址和端口。因此,转换器必须保存每个UDP流的状态,而UDP自身却没有状态。更糟糕的是,NAT设备还被赋予了删除转换记录的责任,但由于UDP没有连接终止确认环节,任何一端随时都有可以停止传输数据,而不必发送通知。为解决这个问题,UDP路由记录会定时过期。定时多长?没有规定,完全取决于转换器的制造商、型号、版本和配置。因此,对于较长时间的UDP通信,有一个事实上的最佳做法,即引入一个双向的keep-alive分组,周期性的重置传输路径上所有NAT设备中转换记录的计时器。
UDP是一个简单常用的协议,经常用于引导其他传输协议。事实上,UDP的特色在于它所省略的那些功能:连接状态、握手、重发、重排、拥塞控制、拥塞预防、流量控制,统统没有。这个面向消息的最简单的传输层在提供灵活性的同时,也给实现着带来了麻烦。你的应用程序很可能需要从头实现上述的几个或大部分功能。如果要使用UDP有以下设计建议:
当然,WebRTC就是满足这些条件的框架!