上一小节,我们学习了传输层的基本原理。这一层有两个非常重要协议: UDP 和 TCP 。这一小节,我们开始学习 UDP 协议。
报文结构
UDP 是 用户数据包协议 ( user datagram protocol )的简称,它是一种简单的数据报式传输层协议。UDP 数据报结构非常简单,头部只包含端口号等若干个字段:
由于 UDP 位于传输层,因而报文有时也称为 UDP 段或 UDP 分组。UDP 报文也分为头部和数据两个部分,结构跟我们在上节讨论的传输层段几乎一模一样。其中,头部只有 4 个字段:
- 源端口( source port ),发送方的端口号;
- 目的端口( destination port ),接收方的端口号;
- 报文长度( length ),即整个 UDP 报文的长度,包括头部和数据,单位为 字节 。
- 检验和( checksum );
UDP 报文需要借助 IP 协议提供的主机通信能力,作为数据搭载在 IP 包中发往目标主机:
长度
那一个 UDP 报文,最多可以承载多少数据呢?
由于报文长度字段为 16 字节,因此 UDP 报文理论上可以达到 $2^{16}$ ,也就是 65535 字节。除去头部,UDP 包最多可以承载的数据量是: $2^{16} - 8$ ,也就是 65527 字节。
但 UDP 报文需要作为数据,搭载在 IP 包中进行网际通信。因此,UDP 报文大小最多也就是 IP 包的最大长度 65535 字节。换句话讲,UDP 报文长度不可能超过 $65535 - 20 -8$ ,也就是 65507 字节。其中,20 是 IP 包头部的最小长度。
校验和
由于 IP 包只对头部做校验和,无法对数据部分进行纠错,因此 UDP 为自己实现校验和。
发送方为 UDP 数据报计算校验和,并将其保存在校验和字段中;接收方收到报文后,重新计算校验和并与报文中的校验和字段进行对比;以此确定报文在传输的过程中是否发生差错。
这个过程与以以太网帧校验和机制大致相同,本无须多言。但还是有一点需要特别注意:
与 IP 校验和不同,UDP 整个报文都会参与校验和计算。除此之外,UDP 还会在报文前面拼接一个 IP 伪头部,同时参与校验和计算。IP 伪头部只用于计算校验和,不会真的发送。IP 伪头部会包含 IP 层的核心信息,包括:
- 源地址
- 目的地址
- 协议类型
- 报文长度
IP 已经有了首部校验和了,UDP 校验和计算为什么还要考虑这些关键字段呢?
答案很简单:再上一道保险。
因为校验和长度是有限的,所以存在多个不同的 IP 包头,对应到同一个校验和的情况。换言之,校验和机制可能会漏判: IP 包头已经发生差错,但计算出来的校验和仍然匹配。
这样的话,系统可能会错收 UDP 包:假设 UDP 报文在传输过程中 IP 头部发生差错,目的地址变了,但 IP 头部校验和刚好揪不出。这时,“目的主机”就会错收这个 UDP 报文。
将 IP 包关键字段纳入 UDP 校验和计算后,就算 IP 层没能揪出 IP 包头部差错,UDP 层也很有可能会发现。当然了, UDP 校验和也可能会漏判,这时就只能由应用层来纠错了。
通信过程
我们回过头来考察一下 UDP 协议的通信过程,以时间同步进程请求时间服务为例:
- 时间同步进程通过 套接字( socket )将请求数据提交给操作系统,套接字负责维护通信双方相关信息,包括地址端口对;
- 操作系统接到数据后,将数据封装到一个 UDP 数据报,头部注明通信双方的端口号,这个信息来自套接字;
- UDP 报文需要借助 IP 包发往目标主机,因此操作系统将 UDP 报文作为数据封装成 IP 包,IP 包头部注明通信双方的地址,这个信息同样来自套接字;
- IP 包发送出去后,将经过若干条中间路由,最终来到目标服务器;
- 服务器协议栈根据 IP 包头中的协议类型字段,取出数据中的 UDP 包,并提交给传输层进一步处理;
- 传输层根据 UDP 报文端口号,找到相关的套接字,并通过套接字将数据提交给对应的时间服务器进程;
顺便提一下, 套接字 是操作系统提供的网络编程接口。
当进程需要进行网络通信时,需要先申请套接字,并进行初始化。初始化时需要设置网络通信的上下文信息,包括通信双方的地址端口对、缓冲区大小等等。
当进程需要发送数据时,将数据提交到套接字。操作系统根据套接字上下文信息,完成报文封装并发送到网络上去。当系统收到网络报文后,根据报文信息找到对应的套接字,数据则由套接字提交给所属的进程。
扩展阅读
【小菜学网络】系列文章首发于公众号【小菜学编程】,敬请关注: