UDP协议sendto消息长度限制

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

先把测试得到的结果列出来:

MacOS 发送buffer长度限制: 1024*9 = 9216 Bytes
CentroOS 发送buffer长度限制: 1024*64 - 28=65507 Bytes (推测应该介于55467-65507之间,取决于IP头部长度,如果IP头部长度>最小值20,带了附加头信息的话,支持长度应该会减少)

另外已知:Ethernet数据链路层的MTU(Maximum Transmisiion Unit)通常是1500Bytes,对于大于MTU的IP包需要拆分发送,发送方主机或路由器会对它进行拆分。所以测试最高支持码流时,大于MTU,IP包会进行拆分在链路上发送,然后会在接受端的主机上重新组转,使用Wireshark抓包时也可以看出来这一点,在底部会有一个reassembled页签。

抓取的码流:

码流说明:从一台外部的Centos系统的主机,发UDP消息到本机的UDP监听服务器
数据链路层(头部:8字节):Ethernet II, Src: Tp-LinkT_1e:16:0c (??:??:??:??:??:??), Dst: Apple_cd:dc:e9 (??:??:??:??:??:??)
    目标Mac地址(本机Mac):Destination: Apple_cd:dc:e9 (??:??:??:??:??:??)
    源Mac地址(本机所连路由器的Mac):Source: Tp-LinkT_1e:16:0c (??:??:??:??:??:??)
    类型:Type: IPv4 (0x0800)

网络层-IP(头部:20个字节):Internet Protocol Version 4, Src: ???.???.???.???, Dst: ???.???.???.???
    版本:0100 .... = Version: 4
    头长度:.... 0101 = Header Length: 20 bytes (5)  根据头部长度的位数4,IP头部最长可以达到15*4=60个字节
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
    头和数据总长度:Total Length: 415 根据位数限制8,头部与数据最大可以达到65535字节,当前值为最后一个分段的长度
    分段标示:Identification: 0x6ce7 (27879)
    Flags: 0x1fcc
    保留0... .... .... .... = Reserved bit: Not set
    不分段.0.. .... .... .... = Don't fragment: Not set
    还有分段..0. .... .... .... = More fragments: Not set
    分段偏移地址:...1 1111 1100 1100 = Fragment offset: 8140 当前分段偏移量为8140*8=65120 字节,代表已传送过的字节数
    生命期:Time to live: 51
    协议:Protocol: UDP (17)
    校验和:Header checksum: 0x9e5d [validation disabled]
    源IP:Source: ???.???.???.???
    目标IP:Destination: ???.???.???.???

传输层UDP(头部:8字节):User Datagram Protocol, Src Port: 34749, Dst Port: 28881
    源端口:Source Port: 34749
    目标端口:Destination Port: 28881
    长度:Length: 65515
    校验和:Checksum: 0xc492 [unverified]
Data (数据部分:65507字节)(65507 bytes)

协议分析:

从协议上来看的话,网络层IP报文头中的Total Length总长度限制8位,为65535长度,当前值为415长度,代表本个分段的IP头及数据长度。
从当前IP报文中分段偏移地址-8140(该字段代表8字节倍数)存在,可以推测出报文被分段fragments传送了,因为展现的是最后一个分段的,所以可以推断出之前已经传送分段数据的长度为:8140 * 8 = 65120字节。
Total Length代表的415字节+分段偏移地址代表已传送数据的65120字节 = 65535字节。
Wireshark上对收到的分段标示Identification一致的分段进行相关的reassembled重新组装,对于IP上层的UDP协议或应用来说,多个分段消息,相当于收到了一个IP包,Total Length组装后修整为65535,已经达到了8位所能支持的极限值了。

协议上对IP包长度限制为65536,对与所发送消息的长度,需要考虑IP头和UDP头所占用长度,IP头介于(20-60字节)长度,UDP头占用8字节。那么所发数据部分的占用长度为65536-[20-40]-8=介于55467-65507之间,应为IP头部通常为最小值20字节,所以数据部分通常限制为为65507。

 

对于MacOS上限制为什么是9216字节,系统设置相关的参数如下,应该是系统做了一些特殊处理,因为对于UDP包来说,一个fragment分组传送有问题,整个package消息就会丢弃,所以采用较小的值,离MTU大小越近越好。
这个参数也可以通过sysctl来进行修改的,但据stackflow上别人讲cmd上的修改重启后就失效了。

command:sysctl -a|grep maxdgram
net.local.dgram.maxdgram: 2048
net.inet.udp.maxdgram: 9216
net.inet.raw.maxdgram: 8192

 

测试用的函数供参考:

#include 
#include 
#include 
#include 
#include 
#include "myerr.h"

#define REQUEST 1024*9 
#define REPLY 256*256 

int main(int argc, char **argv)
{
    struct sockaddr_in serv;
    char request[REQUEST], reply[REPLY];
    int sockfd, n, errno;
    int maxSendCount = 1;    
    if (argc < 3)
    {
        err_quit("usage: %s ipaddress port", argv[0]);
    }
    
    if ((sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
    {
        err_sys("socket error");
    }

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(argv[1]);
    serv.sin_port = htons(atoi(argv[2]));
    
    // send
    {
        sprintf(request, "hello world...");
        if (sendto(sockfd, request, REQUEST, 0, (const sockaddr*)&serv, sizeof(serv)) != REQUEST)
        {
            err_sys("sendto error");
        }
    }

    // receive
    {
        memset(&reply, 0, sizeof(reply));
        if ((n = recvfrom(sockfd, reply, REPLY, 0, NULL, (unsigned int*) NULL)) < 0)
        {
            err_sys("recvfrom error");
        }
        fprintf(stdout, "recv: %s\n", reply);
    }


    return 0;
}

参考:https://stackoverflow.com/questions/3292281/udp-sendto-and-recvfrom-max-buffer-size
参考:《计算机网络(第三版)》
参考:https://stackoverflow.com/questions/22819214/udp-message-too-long
参考:TCP/IP详解

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

 

你可能感兴趣的:(网络)