高性能浏览器网络(High Performance Browser Networking) 第三章

第3章UDP篇

1980年8月,用户数据报协议(UDP)由John Postel添加到到核心网络协议族中,UDP协议起始于TCP/IP协议以后,但和TCP和IP规范被分裂成为两个独立的RFC的时间差很少。这个时机是很是重要的,由于正如咱们将看到的,UDP重要的特色不是他带了什么新特性,而是他忽略了的那些特性。UDP(RFC 768)是通俗称为空协议,它描述的操做,基本上能够容纳在一张餐巾纸上。html

数据报 一个自包含的,独立的数据实体,其承载了足够的信息,使其能够从源路由到达目标路由,而不依赖于在网络节点间和底层传输网络中的前面数据包。算法

数据报文(Datagram)和数据包(Packet)两个术语往交替使用,但其实两者有一些细微差异。数据包(packet)通常用来描述任何格式的数据块,而数据报(Datagram)每每被保留用来描述经过一个不可靠的服务传输的数据包(Packet) - 没有传输保障,没有失败通知。正由于如此,你常常发现有人用不可靠(Unreliable)来替代UDP官方定义中的User,咱们能够理解成“不可靠的数据报协议”。这也是为何UDP包通常或者说更准确的被称为数据报(Datagram)。浏览器

UDP的一个最著名的也是全部浏览器和网络应用都要依赖的应用就是DNS服务,任何一个Host名称,咱们须要在数据交换以前获取它的IP地址。不过,即便是浏览器自己依赖于UDP的,但UDP历来没有做为网页获取和浏览器应用传输协议的第一选择。固然,WebRTC的出现,状况有所变化了。安全

新的Web实时通讯(WebRTC)标准,由IETF和W3C工做组共同制定,实现实时通讯,如语音和视频呼叫,以及其余形式的对等(P2P)通讯,其就是在浏览器中采用了UDP。WebRTC中,UDP是首选的传输协议。咱们将在第18章中深刻讨论WebRTC ,但在此以前,咱们首先探讨一下UDP协议的内部运做,了解一下WebRTC为何选择UDP协议。服务器

 

空协议服务

要了解UDP和为何它一般被称为“空协议”,咱们首先须要了解一下互联网协议(IP),它位于TCP和UDP协议层下面。网络

IP层主要任务就是基于地址将数据报从源主机发送到目的主机。要作到这一点,消息都封装在一个IP包( 图3-1 ),标识源和目的地址,以及一些其余路由参数。并发

咱们再次强调一下上面提到的数据报这个术语的含义:IP层提供了不可靠的数据传输,既没有消息确认,也没有丢失通知, IP层直接把这一层的不可靠性暴露给上层。若是一个数据报在传输过程当中由于某个路由节点拥塞,高负荷,或因其余缘由丢失,那么由IP上层的协议来检测,恢复,并重传数据 - 固然这是在上层有这个需求的时候!框架

 

 

 

图3-1 IPv4报头(20字节)性能

UDP协议在IP包的基础上增长了新的报头( 图3-2 ),它只增长了四个额外的字段:源端口,目的端口,数据包长度,数据校验消息。所以,当IP层传送数据包到目的主机时,主机解开UDP数据包,经过目的端口识别目标应用程序,并发送消息。除此以外,再无其余。优化

 

 

 

图3-2 UDP报头(8字节)

事实上,UDP报头中的源端口和校验字段都是可选字段。IP数据包中已经包含它本身的报头校验和,应用层彻底能够选择忽略UDP的校验字段,这意味着UDP层全部的错误检测和纠错,能够委托给上述应用层校验。其核心,UDP只是在IP层之上提供了“应用层复用”特性,也就是嵌入了源和目标端口。考虑到这一点,咱们如今能够总结UDP全部不能提供的服务:

无消息传输保证 

没有确认,重发,或超时 

没法保证按序传输 

没有数据包的序列号,没有从新排序,无线头阻塞 

无链接状态跟踪 

没有链接创建或关闭的状态机 

没有拥塞控制 

无内置的客户端或网络反馈机制

TCP是一个面向字节流的协议,可以经过多个数据包发送应用程序的消息数据,包内自己没有任何明确的消息边界。为了实现这一目标,链接两端都分配了链接状态,而且数据包被排序,重发丢包,按顺序发送。相反UDP数据报有明确的界限:每个数据报都被打包到一个IP包中,应用层读到的每个UDP包都是完整的信息 - 数据包不能被分割。

UDP是一个简单的,无状态的协议,适合于引导上层的其余应用层协议 - 几乎全部的协议决策都留给它上面的应用层。然而,在你想实现本身的协议来取代TCP,你应该仔细考虑有关的复杂性,如UDP与其它层的交互(好比NAT穿越),以及网络协议一些最佳实践。没有仔细的规划和设计,设计一个新的协议不是一个好主意,最终也许实现成一个的简陋的TCP版本。各类算法和TCP状态机已通过几十年的锤炼和提高,并已采起几十种机制来保证他的性能。

 

UDP和网络地址转换

很是不幸,IPv4地址只有32位长,它提供了最多42.9亿的IP地址。1994年中旬(RFC 1631),IP网络地址转换(NAT)规范,做为一个临时的解决方案,被提出来解决IPv4地址枯竭的问题 - 在上世纪90年代初期,互联网上的主机的数量开始成倍增加,咱们根本没法为每台主机分配一个惟一的IP。

建议的IP重用的解决方案是在边缘网络中引入NAT设备,NAT将负责为维护本地IP和端口的元组到一个或多个全球惟一的(公共)IP地址和端口的元组的映射关系(图3 -3 )。NAT内部的本地IP地址空间能够被许多不一样的子网络重用,从而解决地址耗尽的问题。

 

 

 

图3-3 IP网络地址转换

不幸的也是常常发生的是,临时方案最后老是变成最终方案。NAT设备不只仅用来解决IP地址枯竭的问题,他们也很快成为一个无处不在的网络部件,包括许多企业和家庭代理和路由器,安全设备,防火墙,和几十个其余的硬件和软件设备都包含了NAT功能。NAT再也不是临时方案了,它已经成为互联网基础设施的一个组成部分。

 

 

保留的私人网络地址范围

互联网编号分配机构(IANA),这是一个负责全球IP地址分配的机构,预留了三个著名的私人网络段,常常用在NAT设备内部网络:

 

表3-1. 保留的IP地址段

 

IP地址段

地址数量

10.0.0.0 - 10.255.255.255

16,777,216

172.16.0.0 - 172.31.255.255

1,048,576

192.168.0.0 - 192.168.255.255

65,536

你们应该熟悉上面全部或者部分地址段。基本状况是,本地路由器给您的计算机分配上面某个IP地址段中的一个地址 - 那是你在内部网络的私有IP地址,当与外部网络通讯时,NAT将会作网络地址转换。

为了不路由错误和混乱,公网主机不容许从上面任何这些保留的私有网络范围分配IP地址。

 

链接状态超时

NAT转换的关键问题,至少对UDP而言,是它必须保存数据传输的路由表。NAT依赖网络链接状态,而UDP刚好没有 - 这是一个严重的不匹配,这也为UDP传输问题的根源。此外,如今很普通的一个状况就是NAT内网的设备有不少层,这只会使问题进一步复杂化。

每一个TCP链接有一个明确的协议状态机,开始三次握手,跟着开始数据传输,最后关闭链接,有一个完整的流程。基于这种流程,NAT能够观察到每一个链接状态,并能够根据须要建立和删除的路由条目。而UDP,既没有握手,也没有链接终止,同时没有任何状态机来监控链接状态。

经过UDP往外发送数据并不须要任何额外的工做,但请求的答复却须要NAT维护路由表,用来识别本地目标主机的IP和端口。所以,NAT必须保持每一个UDP流的路由表信息,由于UDP是无状态的。

更糟的是,NAT须要知道何时清除路由记录,但UDP没有链接终止序列,任什么时候候,两端均可以中止发送数据包,不带任何通知。为了解决这个问题,UDP路由记录有一个老化定时器。这个定时器到底多长?”基本上没有明确的答案,而是取决于设备提供商,版本,配置等。所以,事实上长时间运行的UDP会话的最佳实践之一就是引入双向 keepalive报文,按期的重置路由上全部的NAT设备的老化计时器。

 

 

TCP超时和NAT

从技术原理上来讲,在NAT设备没有必要为TCP链接提供超时老化机制。TCP协议有一个良好握手机制和终止序列包,NAT能够清晰的根据这些信息来添加或者删除路由记录。

不幸的是,在实际应用中,许多NAT设备为TCP提供了相似UDP的老化计时器。其结果是,在某些状况下,TCP链接也须要双向Keepalive报文。若是你的TCP链接忽然降低,也许就是NAT设备的老化机制惹的祸。

 

NAT穿越

不可预知的链接状态管理是NAT的一个严重问题,但对于许多应用程序的一个更大的问题是根本没法创建UDP链接。这对不少应用譬如P2P,如VoIP,游戏,文件共享等来讲更是如此。这些应用每每通讯双方须要同时充当客户端和服务端角色,使其能双向通讯。

第一个问题是,在有NAT的场景下,内部客户端不知道它的公网IP​​:它只知道它的内部IP地址,NAT设备对每个UDP数据包进行重写,修改UDP包的源端口和地址,以及IP层的源IP地址。可是,若是客户端将私有IP地址做为应用层数据的一部分与外部网络地址进行通讯,那么链接将不可避免地失败。所以,NAT这种“透明”的转换就有问题了,应用程序必须先发现它的公网IP​​地址,若是它须要与外部网络中的一个地址进行通讯。

然而,仅仅知道的本身的公网IP是没法保证UDP传输成功的。任何数据包到达拥有公网IP的NAT设备后​​,也​​有一个目的端口,NAT路由表中必须有一个外网IP端口与内网地址和端口的映射记录,数据才能真正达到目的地址。若是这个记录不存在,那么数据包被简单地丢弃(图3-4 )。NAT做为一个简单的包过滤器,它没有办法自动肯定内部路由映射关系,除非用户经过端口转发或者相似机制显式在NAT上进行了登记。

 

 

 

图3-4 因为缺乏映射记录,收到的包被丢弃

须要注意的是,上面描述的问题对于客户端应用程序来讲不是一个问题,客户端从内部网络中先发起链接,NAT天然会添加相应的路由。可是,对于那种须要主动接收链接(内网主机做为服务器)的应用如P2P应用(如VoIP),游戏终端,文件共享等等,就会碰到这个问题。

为了解决这种UDP的穿越问题,各类穿越技术(STUN,TURN,ICE)被提出了,用于创建在 两个内网主机之间创建端至端的链接。

 

STUN,TURN,ICE

STUN(RFC 5389)协议是一种容许主机应用程序发现网络中的NAT设备,并借助其来为当前链接分配公网IP​​和端口元组(图3-5 )的方案。要作到这一点,该协议须要借助一个第三方部署在公网上的STUN服务器。

 

 

 

图3-5 STUN查询公网IP和端口

假设STUN服务器的IP地址是可知的(经过DNS发现,或经过手动指定的地址),应用程序首先发送绑定请求到STUN服务器。相应的,STUN服务器回复一个响应,其中包含为其分配的客户端对外暴露的公网IP​​地址和端口。这个简单的流程解决咱们了咱们前面讨论中遇到的几个问题:

 

  • 该程序经过该方式获取了其公网IP和端口的元组,并使用这个信息,做为其应用数据的一部分,就可以与对端进行通讯。
  • 向STUN服务器发送的请求,也同时在NAT上创建了路由映射记录,这确保了对端的请求能够准备达到内部网络中的应用。
  • STUN协议定义了一个简单的机制来保持NAT上的路由老化。

有了这个机制,两端须要经过UDP进行通讯时,他们会先发送绑定请求到各自的STUN服务器,收到各自STUN服务器的响应,而后他们可使用各自分配的公共IP地址和端口进行数据交换了。

然而,在实际应用中,STUN是不足以处理全部的NAT的拓扑结构和网络配置。此外,不幸的是,在某些状况下,UDP可能会被防火墙或其余一些网络设备彻底阻止 - 这种许多企业网络中不是一种罕见的情景。为了解决这个问题,只要STUN失败,咱们还可使用TURN协议(RFC 5766)做为备用方案,它能够运行在UDP上,还能够将UDP转换成TCP。

TRUN方案的关键就是中继(relay)。该协议依赖于公网上的中继来保证私网主机的可见性和可用性( 图3-6 )。

 

 

 

图3-6 TURN中继服务器

 

  • 两端都向相同的TURN服务器发送地址分配请求,其次是权限协商。
  • 一旦协商完成后,两端经过将数据发送到TURN服务器,并由TURN进行转发到对端的方式进行互相通讯。

固然,这种通讯方式的最明显的缺点就是他再也不是P2P的通讯。他须要依赖于TURN服务器来保证可靠的传输,TURN服务器成为一个瓶颈,维护TURN的成本将很高,至少TURN服务器须要足够的带宽来保证全部的数据流。所以,TURN方案最好做为最后的备用方案,只有在其余方案都失效的状况下才能使用。

 

 

STUN和TURN实践

google提供的Libjingle,是一个开放源码的C + +库,能够用它来建立P2P的应用,它在底层实现了STUN,TURN,ICE等协商。这个库用在Google Talk中,库文档为STUN与 TURN在现实世界中的性能提供了有价值的参考点:

 

  • 92%的时间能够直接链接方案(STUN)
  • 8%的时间链接须要一个中转器(TURN)

不幸的是,即便采用STUN方案,有部分用户仍是没法创建直接的P2P隧道。为了提供可靠的服务,咱们还须要TURN中继,它能够做为STUN方案不可用状况下的一个备选方案。

创建一个有效的NAT穿越解决方案,不是一件简单容易的事情。值得庆幸的是,咱们能够借助ICE协议(RFC 5245)来帮助咱们完成这一任务。ICE是一个协议,和一组方法,用来寻求最有效的端与端之间(图3-7 )隧道创建方法:若是可能则直接链接,若是不行则经过STUN进行协商,若是都失败了则采起TURN。

 

 

 

图3-7 ICE试图经过直接链接,STUN和TURN创建链接

在实践中,若是你正在建设一个基于UDP的P2P应用程序,那么你最但愿利用现有的平台API或第三方的库,为您实现ICE,STUN和TURN。如今你应该了解了这些协议,如今你能够跳转到相应的安装和配置去实现你的方案了!

 

UDP优化

UDP是一种简单而经常使用的协议。事实上,UDP的主要特征是它忽略了的功能:无链接状态,握手,重发,重组,从新排序,拥塞控制,拥塞避免,流量控制,甚至可选的错误检查。然而,这个面向消息的传输层能提供的灵活性,也是实现者的责任。您的应用程序可能从头开始从新实现部分或者许多缺失的特性,每个特性都应该需对端或者应用协议匹配。

与TCP不一样,内置了流量和拥塞控制、拥塞避免机制,UDP应用程序必须本身实现这些机制。拥塞不敏感的UDP应用程序能够很容易的拥塞网络,可能会致使网络性能下降,在严重的状况下,会致使网络拥塞崩溃。

若是你想在本身的应用程序中使用UDP,确保研究和阅读当前的最佳实践和建议。在RFC 5405中,特别强调了应用程序经过单播UDP传送数据设计指南。下面是一个简短的例子:

 

  • 应用必须忍受变化的互联网路径
  • 应用应控制传输速率
  • 应用应当实现全部流量拥塞控制
  • 应用应该使用和TCP同等的带宽
  • 应用当丢包时应该回退重传计数器
  • 应用不该该发送超过MTU的数据报
  • 应用应该处理数据报的丢失,重复,从新排序
  • 应用应该是确保能够支持两分钟的延迟
  • 应用应该启用IPv4 UDP校验,必须启用IPv6校验
  • 应用可能在须要的时候使用保活(最小间隔15秒)

设计一个新的传输协议,须要不少的认真思考,规划和研究 - 作你的尽职调查。在可能的状况下,充分利用现有的库或已经采用了一个现有框架来实现NAT穿越,使其可以与其余来源的网络创建某种程度的公平通讯。

关于这一点,好消息, WebRTC就是这样一个框架!

相关文章
相关标签/搜索