通俗易懂使用者资料报协议(UDP)
什么是UDP?
UDP是User Datagram Protocol(使用者资料报协议)的缩写,它是一个简单的协议,简单到UDP规范RFC0768只有区区3页。UDP是工作在IP层之上的传输层协议,UDP对IP主要有两个扩充套件:
扩展出埠号使得IP资料报可以多路分发到使用者程序。扩展出校验和提供网络传输过程中资料差错的检验。IP提供了一种尽力而为、无连线的资料报交付服务。IP基于IP地址实现路由和分组转发,可以将一个IP资料报从网络的一台主机传送到另一台主机,IP地址决定IP资料报将被送往哪个主机。所以,IP提供主机到主机的资料报传输服务。
IP资料报到达目的主机后,核心层实现的IP模组,会负责接收网络卡上的IP资料报,但主机上通常会同时执行多个程序,IP资料报应该交给哪个程序去处理呢?IP搞不定。
埠号(位于UDP首部)决定资料报交给主机上的哪个程序处理。所以,UDP为端主机上执行的应用程序提供了端到端服务。
UDP的特征
UDP是无连线的,通讯之前无须建连便可直接传送资料报,而TCP是面向连线的。UDP不提供差错纠正,但UDP提供差错检测(端到端校验和)。UDP不做重复消除。UDP不做流量控制。UDP不做拥塞控制,没有协议机制防止高速UDP流量对其他网络使用者的消极影响。UDP不保证顺序,资料报递交应用的顺序。UDP不可靠,UDP只负责把应用程序传给IP层的资料传送出去,并不能保证资料报到达目的地,可靠传递需要应用程序去实现。UDP支援组播交付。UDP是一种保留讯息边界的传输层协议。讯息边界
应用程序每请求一次UDP输出将产生一个UDP资料报,从而传送一个IP资料报,而接收端每请求一次UDP接收都将接收一个完整的UDP报(如果有),这跟面向资料流的TCP不一样。假设主机A给主机B传送2次资料,第一次4字节“abcd”,第二次3字节“xyz”,而主机B接收2次,分别返回“abcd”、“xyz”两个讯息,也可以返回“xyz”,“abcd”两个讯息(顺序不重要),那么这就是保留讯息边界。
UDP是保留讯息边界的传输层协议,利用UDP通讯的应用程序每次传送操作会产生一个IP资料报(不考虑分片),这就约束每次传送的资料量不能大于MTU(最大传输单元),接收端每次接收都会返回一个个UDP资料报的完整负载,不会出现返回半个资料报负载的情况。
而TCP是不保留讯息边界的流协议,传送端呼叫传送的次数和每次传送的资料量,跟接收端呼叫接收的次数和每次接收的资料量,没有任何对应关系,所以使用TCP的应用程序需要去处理讯息边界。
UDP资料报封装格式
IPv4协议(Protocol)字段用值17来标识UDP,UDP资料报头部通常是8字节,IPv4头部之后紧接着是UDP头部,然后是UDP资料Payload(如有)。
IPv4 UDP资料报封装格式
IPv4封装包对应的UDP头部由源埠号、目的埠号、长度、校验和组成,每个字段都是2字节。
1、埠号,纯抽象的标识,它不跟任何物理实体相关。
埠号用于帮助协议分辨传送和接收程序。接收端的核心层从网络卡接收到IP资料报之后,识别出UDP资料报(IP资料报头部协议字段值=17)之后,会根据UDP头部的目的埠号,对映到对应程序,把UDP资料报交给对应的程序去处理,这个对映关系由系统核心管理维护。
UDP头部和负载
目的埠号是必须的,但源埠号是可选的,如果资料报传送者不需要对方回复的话,则源埠号可被设定为0。
因为IP层根据IP头部的协议型别字段,将进入的IP资料报,分发到特定的传输协议(TCP或UDP等),到了传输协议层,再根据埠号将协议资料分发到不同程序。所以,埠号是协议独立的,不同协议的相同埠号并不会引起分发混乱。
比如,一台机器上的两个网络服务程序使用相同的IP地址和埠号,但一个使用TCP协议,另一个使用UDP协议,这样是没有问题。
2、长度字段,是以字节为单位的UDP头部和UDP资料的总长度,因为UDP头部长度为8,且空资料的UDP资料报是允许的,这意味着该长度字段值最小为8。UDP长度值是冗余的,因为可以通过IP资料报的总长度减去IP首部的长度推汇出来。
3、校验和,覆盖了UDP首部、UDP资料和一个伪首部,由初始传送方计算,由最终目的方校验,用于判断资料报在网络传输过程中是否出错,比如某一位从1变成了0。
使用UDP的应用程序如何实现可靠传输
众所周知,UDP不可靠、不保证顺序。1、什么叫不可靠?A给B传送一个UDP资料报,该UDP资料报不一定被正确交付给接收端B,但因为网络质量等各种原因,可能丢包,IP资料报是尽力而为的交付,一切随缘。
有没有办法保证,传送的UDP一定到达目的端?Sorry,保证不了,做不到。
那TCP提供的可靠传输是什么意思?TCP提供的可靠传输并不是指不丢包,因为TCP也依赖IP(IP不可靠)实现资料报交付,TCP的可靠性是指丢掉的包会被重传,直到被正确投递,才会继续传输下一个资料报。
那TCP是怎么做到可靠传输的呢?很简单,收报确认(ack)+丢包重传。所以UDP如果要提供可靠传输,也可以参考TCP的实现机制,只是TCP是实现在核心层,而基于UDP的应用程序,可以把可靠传输做到应用层。要做收报确认+丢包重传,需要一些额外的资讯,比如包序列号之类,可以放到Payload,约定好这些额外资讯在Payload中的结构布局即可。
2、什么叫不保证顺序?A给B传送两个UDP资料报,两个UDP资料报会被封装为两个IP资料报,通过IP协议传输,因为两个IP资料报独立路由,所以哪个先到?不一定,看心情。
有没有办法保证,UDP资料报按照发送端传送的时间顺序到达目的端?也Sorry,办不到。
所以,TCP提供的顺序性,其实只是在接收端对IP资料报根据传送顺序重新排序,很显然,UDP要支援重排序,也需要一些额外资讯,也只能通过payload携带,而不能像TCP一样(TCP首部的一些字段用于接收端重排序)。
综上,UDP只提供最简单端主机上应用程序的端到端服务,其他的特性,如果要提供,那请参考TCP的思路去实现吧。
这是有好处的:因为简单,所以开销很小。而某些应用场景,可以容忍丢包、乱序,UDP就是很适合的。保时捷是很好,但拉砖还是用拖拉机吧。
UDP Socket程式设计
UDP Socket网络程式设计的API不多,socket()用于建立套接字,close()用于关闭套接字,sendto()用于传送资料,recvfrom()用于接收资料。bind()顾名思义是系结,TCP可以系结,UDP也可以,bind用于UDP等于告诉核心:这个套接字跟网络远端的一个对子关联上了。以后就可以直接呼叫send()往那个标识的主机上的程序传送UDP资料报了。
而没有bind之前,只能通过sendto()界面(通过引数指定目的地)。UDP套接字recv()返回的是UDP资料报的资料部分(Payload),不包括UDP资料报首部,这是因为UDP首部的字段用于分发或者校验,不需要透传给应用程序。
使用UDP套接字开发的网络应用Server/Client,网络IO相关操作和流程如下图所示:
UDP套接字程式设计