feisky

云计算、虚拟化与Linux技术笔记
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

tcpip详解笔记(10) udp协议

Posted on 2012-10-21 21:26  feisky  阅读(1066)  评论(0编辑  收藏  举报

1. UDP协议

UDP是一种对象数据报的传输层协议,它不提供可靠性,其数据报被封装在IP数据报中,封装格式如下图所示:

13

UDP首部格式如下图所示:

14

源端口号和目的端口号分表表示了发送进程和接收进程

UDP长度字段包括了UDP首部和UDP数据的字节长度

2. UDP检验和

UDP检验和覆盖了UDP首部和UDP数据(IP首部检验和只覆盖了IP首部,不覆盖数据报中的任何数据)。

尽管UDP检验和是可选的,但是它们应该总是在用。

U D P数据报的长度可以为奇数字节,但是检验和算法是把若干个16 bit字相加。解决方法是必要时在最后增加填充字节0,这只是为了检验和的计算。U D P数据报和T C P段都包含一个1 2字节长的伪首部,它是为了计算检验和而设置的。伪首部包含I P首部一些字段。

15

如果发送端没有计算检验和而接收端检测到检验和有差错,那么U D P数据报就要被悄悄地丢弃。不产生任何差错报文(当I P层检测到I P首部检验和有差错时也这样做)。

U D P检验和是一个端到端的检验和。它由发送端计算,然后由接收端验证。其目的是为了发现U D P首部和数据在发送端到接收端之间发生的任何改动。

UDP检验和检测不出交换两个16 bit的差错。

3. IP分片

以太网和802.3对数据帧的长度都有一个限制,其最大值分别是1500和1492个字节。链路层的这个特性称作MTU。不同类型的网络大多数都有一个上限。如果IP层有一个数据要传,且数据的长度比链路层的MTU还大,那么IP层就要进行分片(fragmentation),把数据报分成若干片,这样每一个分片都小于MTU。当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与其他分组独立。

把一份IP数据报进行分片以后,由到达目的端的IP层来进行重新组装,其目的是使分片和重新组装过程对
运输层(TCP/UDP)是透明的。由于每一分片都是一个独立的包,当这些数据报的片到达目的端时有可能会失序,但是在IP首部中有足够的信息让接收端能正确组装这些数据报片。

尽管IP分片过程看起来透明的,但有一点让人不想使用它:即使只丢失一片数据也要重新传整个数据报。

why?因为IP层本身没有超时重传机制------由更高层(比如TCP)来负责超时和重传。当来自TCP报文段的某一片丢失后,TCP在超时后会重发整个TCP报文段,该报文段对应于一份IP数据报(而不是一个分片),没有办法只重传数据报中的一个数据分片。

使用UDP很容易导致IP分片,TCP试图避免IP分片。那么TCP是如何试图避免IP分片的呢?其实说白了,
采用TCP协议进行数据传输是不会造成IP分片的,因为一旦TCP数据过大,超过了MSS,则在传输层会对
TCP包进行分段(如何分,见下文!),自然到了IP层的数据报肯定不会超过MTU,当然也就不用分片了
。而对于UDP数据报,如果UDP组成的IP数据报长度超过了1500,那么IP数据报显然就要进行分片,因为
UDP不能像TCP一样自己进行分段。

MSS(Maxitum Segment Size)最大分段大小的缩写,是TCP协议里面的一个概念

    (1)MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能TCP协议在
建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP
数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。通讯双方会根据双方提
供的MSS值得最小值确定为这次连接的最大MSS值。

        (2)相信看到这里,还有最后一个问题:TCP是如何实现分段的呢?其实TCP无所谓分段,因为每
个TCP数据报在组成前其大小就已经被MSS限制了,所以TCP数据报的长度是不可能大于MSS的,当然由
它形成的IP包的长度也就不会大于MTU,自然也就不用IP分片了。

分片图例

16

分片测试:
C:\Sites>ping -f -l 1473 192.168.32.201
Pinging 192.168.32.201 with 1472 bytes of data:
Reply from 192.168.32.201: bytes=1472 time=1ms TTL=64
Reply from 192.168.32.201: bytes=1472 time=1ms TTL=64
Reply from 192.168.32.201: bytes=1472 time=1ms TTL=64
Reply from 192.168.32.201: bytes=1472 time=1ms TTL=64
Ping statistics for 192.168.32.201:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 1ms, Average = 1ms

C:\Sites>ping -f -l 1473 192.168.32.201 (设置不分片标志)
Pinging 192.168.32.201 with 1473 bytes of data:
Packet needs to be fragmented but DF set.
Packet needs to be fragmented but DF set.
Packet needs to be fragmented but DF set.
Packet needs to be fragmented but DF set.
Ping statistics for 192.168.32.201:
    Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),

4. 发生I C M P不可达差错的另一种情况是,当路由器收到一份需要分片的数据报,而在I P首部又设置了不分片( D F)的标志比特。如果某个程序需要判断到达目的端的路途中最小M T U是多少—称作路径MTU发现机制,那么这个差错就可以被该程序使用。

5. 理论上,UDP数据的最大长度为:65535-20字节IP首部长度-8字节UDP首部长度=65507。但是大多是实现都比这个值小,主要是受限于socket接口以及TCP/IP内核的限制。大部分系统都默认提供了可读写大于8192字节的UDP数据报。

6. 数据包截断

Posix系列的recv、recvfrom、read函数均无法得到数据包被截断的错误消息,只有recvmsg可以得到该消息。

ssize_t recvmsg(int socket, struct msghdr *message, int flags);

如果message->msg_flags & MSG_TRUNC为真,则表示数据包被截断。超出部分被丢弃。 但也有例外,Solaris并不设置MSG_TRUNC,直接丢弃超出部分。
SVR4系统不丢弃超出部分,在后续的读操作中会获取超出部分。

Windows下的recv、recvfrom、WSARecv、WSARecvFrom会返回-1,并设置Last Error为WSAEMSGSIZE。

另外,WSARecvEx函数则是专门用于这方面的一个函数。

int PASCAL FAR WSARecvEx(SOCKET s, char* buf, int len, int* flags);

当*flags & MSG_PARTIAL为真实,表示数据包被截断

Windows下所有超出的数据包都会被丢弃。

7. ICMP源站抑制差错

当目标主机的处理速度赶不上数据接收的速度,因为接受主机的IP层缓存会被占满,所以主机就会发出一个ICMP源站抑制差错报文。

8. UDP服务器设计

UDP协议的某些特性将会影响我们的服务器程序设计,大致总结如下:

(1)关于客户IP和地址:服务器必须有根据客户IP地址和端口号判断数据包是否合法的能力(这似乎要求每一个服务器都要具备)

(2)关于目的地址:服务器必须要有过滤广播地址的能力。

(3)关于数据输入:通常服务器系统的每一个端口号都会和一块输入缓冲区对应,进来的输入根据先来后到的原则等待服务器的处理,所以难免会出现缓冲区溢出的问题,这种情况下,UDP数据包可能会被丢弃,而应用服务器程序本身并不知道这个问题。

(4)服务器应该限制本地IP地址,就是说它应该可以把自己绑定到某一个网络接口的某一个端口上。

无觅相关文章插件,快速提升流量