TCP/IP协议详解卷一:Chapter11 笔记
Chapter 11 UDP:用户数据报协议
用户数据报协议(User Datagram Protocol, UDP)是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。UDP数据报封装成一份IP数据报的格式。
RFC 768是UDP的正式规范。UDP不提供可靠性:它把应用程序传给IP层的数据发送出去,但是并不保证它们能到达目的地。
应用程序必须关心IP数据报的长度。如果它超过网络的MTU,那么就要对IP数据报进行分片。如果需要,源端到目的端之间的每个网络都要进行分片,并不只是发送端主机连接第一个网络才这样做。
11.2节 UDP首部
UDP首部的长度为8字节。UDP数据报的格式如下:
UDP数据报 = 16位源端口号 + 16位目的端口号 + 16位UDP长度 + 16位UDP检验和 + 数据(如果有)
端口号表示发送进程和接收进程。由于IP层已经把IP数据报分配给TCP或UDP(根据IP首部中协议字段值),因此TCP端口号由TCP来查看,而UDP端口号由UDP来查看。TCP端口号与UDP端口号相互独立。
UDP长度字段指的是UDP首部和UDP数据的字节长度。该字段的最小值为8字节。IP数据报长度指的是数据报全长,因此UDP数据报长度是全长减去IP首部的长度。
11.3节 UDP检验和
UDP检验和覆盖UDP首部和UDP数据(IP首部的检验和只覆盖IP首部)。UDP和TCP在首部中都有覆盖它们首部和数据的检验和。UDP的检验和是可选的,而TCP的检验和是必需的。
UDP检验和的基本计算方法与IP首部检验和计算方法相类似(16 bit字的二进制反码和),但是它们之间存在不同的地方:
- UDP数据报的长度可以为奇数字节,但是检验和算法是把若干个16 bit字相加。解决方法是必要时在最后增加填充字节0。
- UDP数据报和TCP段都包含一个12字节长的伪首部,它是为了计算检验和而设置的。伪首部包含IP首部一些字段。其目的是让UDP两次检查数据是否已经正确到达目的地。
包含填充字节和伪首部的UDP数据报格式如下:
如果传送的检验和为0,说明发送端没有计算检验和。如果发送端没有计算检验和而接收端检测到检验和有差错,那么UDP数据报就要被悄悄地丢弃,不产生任何差错报文(当IP层检测到IP首部检验和有差错时也这样做)。
11.5节 IP分片
物理网络层一般要限制每次发送数据帧的最大长度。任何时候IP层接收到一份要发送的IP数据报时,它要判断向本地哪个接口发送数据(选路),并查询该接口获得其MTU。IP把MTU与数据报长度进行比较,如果需要则进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上。
把一份IP数据报分片以后,只有到达目的地才进行重新组装(这里的重新组装与其他网络协议不同,它们要求在下一站就进行进行重新组装,而不是在最终的目的地)。重新组装由目的端的IP层来完成。
IP首部 = 4位版本 + 4位首部长度 + 8位服务类型 + 16位总长度 + 16位标识 + 3位标志 + 13位片偏移 + 8位生存时间 + 8位协议 + 16位首部检验和 + 32位源IP地址 + 32位目的IP地址
对于发送端发送的每份IP数据报来说:
- 其标识字段都包含一个唯一值。该值在数据报分片时被复制到每个片中。
- 标志字段用其中一个比特来表示“更多的片”。除了最后一片外,其他每个组成数据报的片都要把该比特置1。
- 片偏移字段指的是该片偏移原始数据报开始处的位置。
- 当数据报被分片后,每个片的总长度值要改为该片的长度值。
- 标志字段中有一个比特称作不分片位。如果将这一比特置1,IP将不对数据报进行分片。相反把数据报丢弃并发送一个ICMP差错报文(“需要进行分片但设置了不分片比特”)给起始端。
当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与其他分组独立。
为什么即使只丢失一片数据也要重传整个数据报?因为IP层本身没有超时重传的机制——由更高层来负责超时和重传(TCP有超时和重传机制,但UDP没有。一些UDP应用程序本身也执行超时和重传)。当来自TCP报文段的某一片丢失后,TCP在超时后会重发整个TCP报文段,该报文段对应于一份IP数据报。没有办法只重传数据报中的一个数据报片。
一个例子
在一个以太网上,数据帧的最大长度是1500字节,其中1472字节留给数据,假定IP首部为20字节, UDP首部为8字节。我们分别以数据长度为1471, 1472, 1473和1474字节运行sock程序,最后两次应该发生分片。相应的tcpdump输出如下:
主机名bsdi后的四位数字是源端的UDP端口号。udp后的数字代表数据长度(不包括UDP首部)。后两份UDP数据报发生了分片。第3行和第4行的frag 26304 以及第5行和第6行的 frag 26313 指的是IP首部中标识字段的值。
分片信息中的下一个数字,即第3行中位于冒号和@号之间的1480,是除IP首部外的片长。两份数据报第一片的长度均为1480:UDP首部占8字节,用户数据占1472字节(加上IP首部的20字节分组长度正好为1500字节)。第1份数据报的第2片(第4行)只包含1字节数据,即剩下的用户数据。第2份数据报的第2片(第6行)包含剩下的2字节用户数据。在分片时,除最后一片外,其他每一片中的数据部分(除IP首部外的其余部分)必须是8字节的整数倍。
位于@符号后的数字是从数据报开始处计算的片偏移值。两份数据报第1片的偏移值均为0(第3行和第5行),第2片的偏移值为1480(第4行和第6行)。跟在偏移值后面的加号对应于IP首部中3 bit标志字段中的“更多片”比特。设置这一比特的目的是让接收端知道在什么时候完成所有的分片组装。
注意第4行和第6行(不是第1片)省略了协议名( UDP)、源端口号和目的端口号。协议名是可以打印出来的,因为它在IP首部并被复制到各个片中。但是,端口号在UDP首部,只能在第1片中被发现。任何运输层首部只出现在第1片数据中。
11.6节 ICMP不可达差错(需要分片)
当路由器收到一份需要分片的数据报,而在IP首部又设置了不分片( DF)的标志比特时,就会发生ICMP不可达差错。如果某个程序需要判断到达目的端的路途中最小MTU是多少(称作路径MTU发现机制),那么这个差错就可以被该程序使用。
此时对应的ICMP差错报文格式为:
1字节类型(=3)+ 1字节代码(=4)+ 2字节校验和 + 2字节未用(置0)+ 2字节下一站网络的MTU + IP首部(包括选项)+ 原始IP数据报中数据的前8字节
11.10节 最大UDP数据报长度
理论上,IP数据报的最大长度是65535字节,这是由IP首部16比特总长度字段所限制的。去除20字节的IP首部和8个字节的UDP首部, UDP数据报中用户数据的最长长度为65507字节。
但是,大多数实现所提供的长度比这个最大值小。第一,应用程序可能会受到其程序接口的限制;第二个限制来自于TCP/IP的内核实现。在许多UDP应用程序的设计中,其应用程序数据被限制成512字节或更小。
由于IP能够发送或接收特定长度的数据报并不意味着接收应用程序可以读取该长度的数据。因此, UDP编程接口允许应用程序指定每次返回的最大字节数。如果接收到的数据报长度大于应用程序所能处理的长度,典型的Berkeley版socket API对数据报进行截断,并丢弃任何多余的数据。SVR4下的socket API(包括Solaris 2.x)并不截断数据报。超出部分数据在后面的读取中返回。
11.11节 ICMP源站抑制差错
可以使用UDP产生ICMP源站抑制(source quench)差错。当一个系统(路由器或主机)接收数据报的速度比其处理速度快时,可能产生这个差错。
ICMP源站抑制报文 = 1字节类型(=4)+ 1字节代码(=0)+ 2字节检验和 + 4字节未用(置0)+ IP首部(包括选项)+ 原始IP数据报中数据的前8字节
11.12 UDP服务器的设计
客户IP地址及端口号
来自客户的是UDP数据报。IP首部包含源端和目的端IP地址,UDP首部包含了源端和目的端的UDP端口号。当一个应用程序接收到UDP数据报时,操作系统必须告诉它是谁发送了这份消息,即源IP地址和端口号。这个特性允许一个交互UDP服务器对多个客户进行处理。给每个发送请求的客户发回应答。
目的IP地址
一些应用程序需要知道数据报是发送给谁的,即目的IP地址。这要求操作系统从接收到的UDP数据报中将目的IP地址交给应用程序。
UDP输入队列
大多数UDP服务器是交互服务器。单个服务器进程对单个UDP端口上(服务器上的知名端口)的所有客户请求进行处理。通常程序所使用的每个UDP端口都与一个有限大小的输入队列相联系。来自不同客户的差不多同时到达的请求将由UDP自动排队。接收到的UDP数据报以其接收顺序交给应用程序。
然而,排队溢出造成内核中的UDP模块丢弃数据报的可能性是存在的。应用程序并不知道其输入队列何时溢出。只是由UDP对超出数据报进行丢弃处理。从tcpdump输出结果可以看到,没有发回任何信息告诉客户其数据报被丢弃。这里不存在像ICMP源站抑制这样发回发送端的消息。最后,看来UDP输出队列是FIFO(先进先出)的。
限制本地IP地址
大多数UDP服务器在创建UDP端点时都使其本地IP地址具有通配符(wildcard)的特点。这就表明进入的UDP数据报如果其目的地为服务器端口,那么在任何本地接口均可接收到它。
当服务器创建端点时,它可以把其中一个主机本地IP地址包括广播地址指定为端点的本地IP地址。只有当目的IP地址与指定的地址相匹配时,进入的UDP数据报才能被送到这个端点。用我们的sock程序,如果在端口号之前指定一个IP地址,那么该IP地址就成为该端点的本地IP地址。例如:
sun % sock -u -s 140.252.1.29 7777
就限制服务器在SLIP接口(150.252.1.29)处接收数据报。对应的netstat输出为:
如果我们试图在以太网上的主机bsdi以地址140.252.13.35向该服务器发送一份数据报,那么将返回一个ICMP端口不可达差错。
有可能在相同的端口上启动不同的服务器,每个服务器具有不同的本地IP地址。但是,一般必须告诉系统应用程序重用相同的端口号没有问题。在sock程序中是通过 -A 选项来完成的。
在主机sun上,可以在同一个端口号( 8888)上启动5个不同的服务器:
对应的netstat输出如下:
在这种情况下,到达服务器的数据报中,只有带星号的本地IP地址,其目的地址为140.252.1.255,因为其他4个服务器占用了其他所有可能的IP地址。如果存在一个含星号的IP地址,那么就隐含了一种优先级关系。如果为端点指定了特定IP地址,那么在匹配目的地址时始终优先匹配该IP地址。只有在匹配不成功时才使用含星号的端点。
限制远端IP地址
在前面所有的netstst输出结果中,远端IP地址和远端端口号都显示为* . *,其意思是该端点将接受来自任何IP地址和任何端口号的UDP数据报。大多数系统允许UDP端点对远端地址进行限制。
sock程序用 -f 选项来指定远端IP地址和端口号:
sun % sock -u -s -f 140.252.13.35.4444 5555
这样就设置了远端IP地址140.252.13.35(即主机bsdi)和远端端口号4444 。服务器的有名端口号为5555。在有的机器上,如果运行netstat命令,我们发现本地IP地址也被设置了,尽管我们没有指定。
UDP服务器本身可以创建的三类地址绑定如下:
在所有情况下,lport指的是服务器有名端口号,localIP必须是本地接口的IP地址。表中这三行的排序是UDP模块在判断用哪个端点接收数据报时所采用的顺序。最为确定的地址(第一行)首先被匹配,最不确定的地址(最后一行IP地址带有两个星号)最后进行匹配。
每个端口有多个接收者
大多数的系统在某一时刻只允许一个程序端点与某个本地IP地址及UDP端口号相关联。当目的地为该IP地址及端口号的UDP数据报到达主机时,就复制一份传给该端点。
当UDP数据报到达的目的IP地址为广播地址或多播地址,而且在目的IP地址和端口号处有多个端点时,就向每个端点传送一份数据报的复制(端点的本地IP地址可以含有星号,它可匹配任何目的IP地址)。但是,如果UDP数据报到达的是一个单播地址,那么只向其中一个端点传送一份数据报的复制。
本文深入解析UDP协议,涵盖UDP首部、检验和、IP分片、最大数据报长度等核心概念,探讨UDP服务器设计与ICMP差错处理机制。
893

被折叠的 条评论
为什么被折叠?



