为更好地理解 UDP 协议的工作原理,本节我们安排一次实验,来观察 UDP 应用的通信过程。
Client-Server 模型
开始之前,我们先来讨论 客户端-服务端模型 ( client-server model ),简称 CS模型 。
网络应用一般可以分为两种角色,一种角色负责提供服务或资源,称为 服务端 ( server );另一种角色则是服务和资源的访问者,一般称为 客户端 ( client )。
如下图,这是一个典型的例子:右边是一台 服务器 ,它负责提供数据服务;左边的手机、PC等设备则是 客户端 ,它们可以通过互联网访问服务器提供的数据:
像这样由客户端和服务端组成的通信模型就叫做 CS模型 ,这种架构在分布式系统中非常常见。
典型应用
我们提供了一个简单的网络时间查询应用,它分为服务端和客户端两部分:
- 服务端 udp-time-server 监听某个端口上的 UDP 请求报文,并提供时间查询服务;
- 客户端 udp-time-client 负责向服务端发起时间查询请求,并输出结果;
客户端和服务端之间采用 UDP 作为通信协议:
- udp-time-client 先发出一个时间查询请求,该请求被封装成 UDP 数据报发给服务端;
- udp-time-server 对请求进行处理,应答数据同样被封装成 UDP 数据报发给客户端;
客户端和服务端双方使用 UDP 协议进行通信,它们必须对 UDP 数据的格式达成一致:
这个应用非常简单,通信数据只分为 请求 ( request )和 应答 ( reply )两种。
其中,请求由客户端发起,它需要提供时间格式化字符串,将期望的时间格式告诉服务端。请求数据分为两个字段:
- 时间格式化字符串长度
- 时间格式化字符串
应答由服务端发出,它需要将时间按照期望的格式,发给客户端。应答数据也分为两个字段:
- 时间字符串长度
- 时间字符串
请求数据和应答数据,都要符合这样的格式才能被通信双方所接受。关于这部分数据格式的约定,其实就构成了 应用层 协议。理论上,我们定的这个数据格式,就构成了一个应用层协议,虽然它还非常简陋。
最后,我们回过头来看:怎么将时间查询应用跑起来。
服务端
在 Linux 终端中执行 udp-time-server 命令,即可启动时间查询应用的服务端:
|
|
- -p 选项指定监听端口。
客户端
同理,在 Linux 终端中执行 udp-time-client 命令,即可运行时间查询客户端:
|
|
- -i 选项指定服务端的 IP 地址;
- -p 选项指定服务端的端口;
- -f 选项指定期望的时间格式,可使用形如
%Y-%m-%d %H:%M:%S
的时间格式化串;
实验步骤
我们需要准备一台服务器和一台客户端主机,之前介绍 traceroute 的演示环境,就是一个不错的选择:
实验环境同样由 Docker 提供,只需执行这个 docker 命令即可一键启动:
|
|
实验环境启动后,我们将看到 3 个窗口。其中,有两个窗口属于主机 ant ,另一个窗口属于主机 apple 。
如何在不同窗口间进行切换呢? 我们来复习一下 tmux 窗口切换操作:
- 按下 tmux 功能键
Ctrl-B
;- 再按下
Q
,这时每个窗口会出现一个数字;- 按下窗口上的数字,即可切到对应的窗口;
我们在主机 apple 上运行服务端 udp-time-server ,-p 选项指定监听端口为 12345 :
|
|
运行客户端之前,我们先使用 tcpdump 命令进行抓包,以便观察双方的通信过程:
|
|
请注意,tcpdump 命令最后的参数是过滤规则,规则
udp
表示只抓 UDP 协议报文。
准备完毕后,我们在主机 ant 上执行客户端 udp-time-client ,向位于主机 apple 的服务端请求时间:
|
|
- -i 指定服务端的地址,即 10.0.2.2 ;
- -p 指定服务端监听的端口,即 12345 ;
- -f 指定想要的时间格式;
udp-time-client 命令的输出表明,它收到了服务端回复的应答,当前时间是 2021-03-16 20:30:52
。
服务端 udp-time-server 的输出表明,它收到了来自 10.0.1.2 ,也就是 ant 的请求:
|
|
主机 ant 上的 tcpdump 程序输出也表明,它向 10.0.2.2( apple )发出了一个 UDP 数据报,报文中带着时间查询请求;随后收到 10.0.2.2 回复的 UDP 数据报,报文中是一个时间查询应答。
|
|
tcpdump 输出看着有点费劲,但我们可以用 Wireshark 来解读,pcap 文件同样可以从 Github 上获取。
WireShark 显示了两个 UDP 报文,其中一个是时间查询请求,另一个是时间查询应答。点击 UDP 协议部分,可以查看 UDP 报文的头部信息;点击 Data ,可以查看 UDP 报文中的数据。
图中高亮的部分就是 UDP 报文中的数据,也就是时间查询请求:前四个字节 00000012
就是时间格式化字符串的长度,十六进制 12 换算成十进制是 18 。后面紧跟着时间格式化字符串%Y-%m-%d %H:%M:%S\0
,包含 \0
在内,正好就是 18 字节。
【小菜学网络】系列文章首发于公众号【小菜学编程】,敬请关注: