速读原著-TCP/IP(UDP服务器的设计)

第11章 UDP:用户数据报协议

11.12 UDP服务器的设计

使用U D P的一些蕴含对于设计和实现服务器会产生影响。通常,客户端的设计和实现比服务器端的要容易一些,这就是我们为什么要讨论服务器的设计,而不是讨论客户端的设计的原因。典型的服务器与操作系统进行交互作用,而且大多数需要同时处理多个客户。

通常一个客户启动后直接与单个服务器通信,然后就结束了。而对于服务器来说,它启动后处于休眠状态,等待客户请求的到来。对于 U D P来说,当客户数据报到达时,服务器苏醒过来,数据报中可能包含来自客户的某种形式的请求消息。

在这里我们所感兴趣的并不是客户和服务器的编程方面( [Stevens 1990]对这些方面的细节进行了讨论),而是U D P那些影响使用该协议的服务器的设计和实现方面的协议特性(我们在 1 8 . 11节中对T C P服务器的设计进行了描述)。尽管我们所描述的一些特性取决于所使用U D P的实现,但对于大多数实现来说,这些特性是公共的。

11.12.1 客户IP地址及端口号

来自客户的是 U D P数据报。I P首部包含源端和目的端 I P地址,U D P首部包含了源端和目的端的U D P端口号。当一个应用程序接收到 U D P数据报时,操作系统必须告诉它是谁发送了这份消息,即源I P地址和端口号。

这个特性允许一个交互 U D P服务器对多个客户进行处理。给每个发送请求的客户发回应答。

11.12.2 目的IP地址

一些应用程序需要知道数据报是发送给谁的,即目的 I P地址。例如,Host RequirementsR F C规定,T F T P服务器必须忽略接收到的发往广播地址的数据报(我们分别在第 1 2章和第1 5章对广播和T F T P进行描述)。

这要求操作系统从接收到的 U D P数据报中将目的 I P地址交给应用程序。不幸的是,并非所有的实现都提供这个功能。
socket API以I P _ R E C V D S TADDR socket选项提供了这个功能。对于本文中使用的系统,只有B S D / 3 8 6、4 . 4 B S D和AIX 3.2.2支持该选项。S V R 4、SunOS 4.x和Solaris 2.x都不支持该选项。

11.12.3 UDP输入队列

我们在1 . 8节中说过,大多数 U D P服务器是交互服务器。这意味着,单个服务器进程对单个U D P端口上(服务器上的名知端口)的所有客户请求进行处理。

通常程序所使用的每个 U D P端口都与一个有限大小的输入队列相联系。这意味着,来自不同客户的差不多同时到达的请求将由 U D P自动排队。接收到的 U D P数据报以其接收顺序交给应用程序(在应用程序要求交送下一个数据报时)然而,排队溢出造成内核中的 U D P模块丢弃数据报的可能性是存在的。可以进行以下试验。我们在作为U D P服务器的b s d i主机上运行s o c k程序:

bsdi % sock -s -u -v -E -R256 -P30 6666
from 140.252.13.33, to 140.252.13.63: 1111111111 从s u n发送到广播地址
from 140.252.13.34, to 140.252.13.35: 4444444444444 从s v r 4发送到单播地址

我们指明以下标志: - s表示作为服务器运行, - u表示 U D P,- v表示打印客户的 I P地址,- E表示打印目的I P地址(该系统支持这个功能)。另外,我们将这个端口的 U D P接收缓存设置为2 5 6字节(- R),其每次应用程序读取的大小也是这个数( - r)。标志- P 3 0表示创建U D P端口后,先暂停 3 0秒后再读取第一个数据报。这样,我们就有时间在另两台主机上启动客户程序,发送一些数据报,以查看接收队列是如何工作的。

服务器一开始工作,处于其 3 0秒的暂停时间内,我们就在 s u n主机上启动一个客户,并发送三个数据报:

sun % sock -u -v 140.252.13.63 6666 到以太网广播地址
connected on 140.252.13.33.1252 to 140.252.13.63.6666
1 1 1 1 1 1 1 1 1 1 1 1字节的数据(新行)
2 2 2 2 2 2 2 2 2 1 0字节的数据(新行)
3 3 3 3 3 3 3 3 3 3 3 1 2字节的数据(新行)

目的地址是广播地址( 1 4 0 . 2 5 2 . 1 3 . 6 3)。我们同时也在主机 s v r 4上启动第2个客户,并发送另外三个数据报:

svr4 % sock -u -v bsdi 6666
connected on 0.0.0.0.1042 to 140.252.13.35.6666
4 4 4 4 4 4 4 4 4 4 4 4 4 1 4字节的数据(新行)
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6字节的数据(新行)
6 6 6 6 6 6 6 6 9字节的数据(新行)

首先,我们早些时候在 b s d i上所看到的结果表明,应用程序只接收到 2个数据报:来自s u n的第一个全1报文,和来自s v r 4的第一个全4报文。其他4个数据报看来全被丢弃。

图11 - 2 0给出的t c p d u m p输出结果表明,所有 6个数据报都发送给了目的主机。两个客户的数据报以交替顺序键入:第一个来自 s u n,然后是来自s v r 4的,以此类推。同时也可以看出,全部6个数据报大约在1 2秒内发送完毕,也就是在服务器休眠的 3 0秒内完成的。
在这里插入图片描述
我们还可以看到,服务器的- E选项使其可以知道每个数据报的目的 I P地址。如果需要,它可以选择如何处理其接收到的第一个数据报,这个数据报的地址是广播地址。

我们可以从本例中看到以下几个要点。首先,应用程序并不知道其输入队列何时溢出。只是由U D P对超出数据报进行丢弃处理。同时,从 t c p d u m p输出结果,我们看到,没有发回任何信息告诉客户其数据报被丢弃。这里不存在像 I C M P源站抑制这样发回发送端的消息。最后,看来 U D P输出队列是 F I F O(先进先出)的,而我们在 11 . 9节中所看到的 A R P输入却是L I F O(后进先出)的。

11.12.4 限制本地IP地址

大多数U D P服务器在创建U D P端点时都使其本地 I P地址具有通配符( w i l d c a r d )的特点。这就表明进入的 U D P数据报如果其目的地为服务器端口,那么在任何本地接口均可接收到它。例如,我们以端口号7 7 7启动一个U D P服务器:

sun % sock -u -s 7777

然后,用n e t s t a t命令观察端点的状态:

sun % netstat -a -n -f inet
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
udp 0 0 *.7777 *.*

这里,我们删除了许多行,只保留了其中感兴趣的东西。 - a选项表示报告所有网络端点的状态。- n选项表示以点数格式打印 I P地址而不用D N S把地址转换成名字,打印数字端口号而不是服务名称。-f inet选项表示只报告T C P和U D P端点。

本地地址以* . 7 7 7 7格式打印,星号表示任何本地 I P地址。当服务器创建端点时,它可以把其中一个主机本地 I P地址包括广播地址指定为端点的本地I P地址。只有当目的 I P地址与指定的地址相匹配时,进入的 U D P数据报才能被送到这个端点。用我们的s o c k程序,如果在端口号之前指定一个 I P地址,那么该I P地址就成为该端点的本地I P地址。例如:

sun % sock -u -s 140.252.1.29 7777

就限制服务器在S L I P接口( 1 4 0 . 2 5 2 . 1 . 2 9 )处接收数据报。n e t s t a t输出结果显示如下:

Proto Recv-Q Send-Q Local Address Foreign Address (state)
udp 0 0 140.252.1.29.7777 *.*

如果我们试图在以太网上的主机 b s d i以地址1 4 0 . 2 5 2 . 1 3 . 3 5向该服务器发送一份数据报,那么将返回一个 I C M P端口不可达差错。服务器永远看不到这份数据报。这种情形如图 11 - 2 1所示。
在这里插入图片描述
有可能在相同的端口上启动不同的服务器,每个服务器具有不同的本地 I P地址。但是,一般必须告诉系统应用程序重用相同的端口号没有问题。

使用sockets API时,必须指定S O _ R E U S E A D D R s o c k e t选项。在s o c k程序中是通过-A选项来完成的。

在主机s u n上,可以在同一个端口号( 8 8 8 8)上启动5个不同的服务器:
在这里插入图片描述
除了第一个以外,其他的服务器都必须以- A选项启动,告诉系统可以重用同一个端口号。5个服务器的n e t s t a t输出结果如下所示:
在这里插入图片描述
在这种情况下,到达服务器的数据报中,只有带星号的本地 I P地址,其目的地址为1 4 0 . 2 5 2 . 1 . 2 5 5,因为其他4个服务器占用了其他所有可能的 I P地址。

如果存在一个含星号的 I P地址,那么就隐含了一种优先级关系。如果为端点指定了特定I P地址,那么在匹配目的地址时始终优先匹配该 I P地址。只有在匹配不成功时才使用含星号的端点。

11.12.5 限制远端IP地址

在前面所有的 n e t s t a t输出结果中,远端 I P地址和远端端口号都显示为 * . *,其意思是该端点将接受来自任何 I P地址和任何端口号的 U D P数据报。大多数系统允许 U D P端点对远端地址进行限制。

这说明端点将只能接收特定 I P地址和端口号的 U D P数据报。s o c k程序用- f选项来指定远端I P地址和端口号:

sun % sock -u -s -f 140.252.13.35.4444 5555

这样就设置了远端I P地址1 4 0 . 2 5 2 . 1 3 . 3 5(即主机b s d i)和远端端口号4444 。服务器的有名端口号为5 5 5 5。如果运行n e t s t a t命令,我们发现本地I P地址也被设置了,尽管我们没有指定。

Proto Recv-Q Send-Q Local Address Foreign Address (state)
udp 0 0 140.252.13.33.5555 140.252.13.35.4444

这是在伯克利派生系统中指定远端 I P地址和端口号带来的副作用:如果在指定远端地址时没有选择本地地址,那么将自动选择本地地址。它的值就成为选择到达远端 I P地址路由时将选择的接口 I P地址。事实上,在这个例子中, s u n在以太网上的 I P地址与远端地址1 4 0 . 2 5 2 . 1 3 . 3 3相连。

图11 - 2 2总结了U D P服务器本身可以创建的三类地址绑定。
在这里插入图片描述
在所有情况下,l p o r t指的是服务器有名端口号, l o c a l I P必须是本地接口的I P地址。表中这三行的排序是 U D P模块在判断用哪个端点接收数据报时所采用的顺序。最为确定的地址(第一行)首先被匹配,最不确定的地址(最后一行 I P地址带有两个星号)最后进行匹配。

11.12.6 每个端口有多个接收者

尽管在R F C中没有指明,但大多数的系统在某一时刻只允许一个程序端点与某个本地 I P地址及U D P端口号相关联。当目的地为该 I P地址及端口号的 U D P数据报到达主机时,就复制一份传给该端点。端点的I P地址可以含星号,正如我们前面讨论的那样。

例如,在SunOS 4.1.3中,我们启动一个端口号为 9 9 9 9的服务器,本地I P地址含有星号:

sun % sock -u -s 9999

接着,如果启动另一个具有相同本地地址和端口号的服务器,那么它将不运行,尽管我们指定了- A选项:

sun % sock -u -s 9999 我们预计它会失败
can't bind local address: Address already in use
sun % sock -u -s -A 9999 因此,这次尝试- A参数
can't bind local address: Address already in use

在一个支持多播的系统上(第 1 2章),这种情况将发生变化。多个端点可以使用同一个 I P地址和 U D P端口号,尽管应用程序通常必须告诉 A P I是可行的(如,用 - A标志来指明S O _ R E U S E A D D R s o c k e t选项)。4 . 4 B S D支持多播传送,需要应用程序设置一个不同的s o c k e t选项(S O _ R E U S E P O R T)以允许多个端点共享同一个端口。另外,每个端点必须指定这个选项,包括使用该端口的第一个端点。

当U D P数据报到达的目的 I P地址为广播地址或多播地址,而且在目的 I P地址和端口号处有多个端点时,就向每个端点传送一份数据报的复制(端点的本地 I P地址可以含有星号,它可匹配任何目的I P地址)。但是,如果U D P数据报到达的是一个单播地址,那么只向其中一个端点传送一份数据报的复制。选择哪个端点传送数据取决于各个不同的系统实现。

发布了1489 篇原创文章 · 获赞 1394 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/weixin_42528266/article/details/104710480
今日推荐