【问题标题】:UDP buffer overflow w/o filling the receive buffer?没有填充接收缓冲区的UDP缓冲区溢出?
【发布时间】:2013-05-08 11:16:04
【问题描述】:

如果我发送 1000 个“Hello World!” UDP 消息(12 个字节 + 28 个 IP/UDP 开销),我观察到在接收端我只缓冲 658 个(总是相同的数字,658*40 = 26320 个字节)。我这样做是通过在服务器上休眠时发送 UDP 消息(在创建套接字之后)。

奇怪的是服务器上的 SO_RCVBUF 选项是 42080 字节。所以,我想知道为什么我不能缓冲 1000 条消息。你知道剩下的 15760 字节都花在哪里了吗?

在服务器代码下方(其中 distrib.h 包含套接字和信号处理函数的基本错误处理包装器):

#include "distrib.h"

static int count;
static void sigint_handler(int s) {
  printf("\n%d UDP messages received\n",count);
  exit(0);
}

int main(int argc, char **argv) 
{
  struct addrinfo* serverinfo;
  struct addrinfo hints;
  struct sockaddr_storage sender;
  socklen_t len;
  int listenfd,n;
  char buf[MAXLINE+1];

  if (argc != 2) {
    log_error("usage: %s <port>\n", argv[0]);
    exit(1);
  }

  Signal(SIGINT,sigint_handler);

  bzero(&hints,sizeof(hints));
  hints.ai_family = AF_INET;    
  hints.ai_socktype = SOCK_DGRAM; 
  hints.ai_protocol = IPPROTO_UDP;
  Getaddrinfo("127.0.0.1", argv[1], &hints, &serverinfo);

  listenfd = Socket(serverinfo->ai_family, serverinfo->ai_socktype, 
            serverinfo->ai_protocol);
  Bind(listenfd, serverinfo->ai_addr,serverinfo->ai_addrlen);
  freeaddrinfo(serverinfo);

  count =0;
  sleep(20); 
  while(true) {
    bzero(buf,sizeof(buf));
    len = sizeof(sender);
    n = Recvfrom(listenfd, buf, MAXLINE, 0, (struct sockaddr*)&sender,&len);
    buf[n]='\0';
    count++;

  }

  close(listenfd);

  return 0;
}

【问题讨论】:

  • MAXLINE的值是多少?
  • 您确定所有消息都已发送吗?服务器和客户端程序是否在同一台计算机/网络上?
  • 如果你在 Linux 上,你检查过 netstat -su ref
  • 除了 socket recv 缓冲区大小之外,可能还有很多其他设置会影响排队数据包的数量。例如,net.core.netdev_max_backlog 是数量可能存在的未处理的输入数据包。
  • @thuovilla: MAXLINE = 1024。

标签: c sockets network-programming udp distributed


【解决方案1】:

进行反向计算会提供更多信息 - 您的缓冲区是 42080,它在开始丢弃之前正在缓冲 658 个数据包。现在 42080/658 = 63.95,所以看起来它会将每个数据包计数为 64 个字节,如果到目前为止缓冲的数据包的总大小等于或高于限制,则丢弃数据包。由于它缓冲了整个数据包,因此它实际上最终缓冲的数量略多于限制。

为什么是 64 字节而不是 40 字节?也许它包括一些排队开销,或者它可能是为了对齐而四舍五入到 2 的某个幂的倍数,或者可能是两者的某种组合。

【讨论】:

  • socket (7) 手册中也提到 50% 的 rcvbuf 会被保留用于记账,所以理论上你不应该依赖超过 50% 的 skb。
【解决方案2】:

我没有完整的答案,但我在我的 Linux 机器上对此进行了测试,这是我观察到的。

当我发送 one "Hello World!\n" 时以 '0' 结尾。我明白了:

客户:

$./sendto
sent 14 bytes

套接字“Recv-Q”有 768 个字节(似乎可能是它的字节数,没有检查 ss 源):

$ ss -ul|grep 55555
UNCONN     768    0               127.0.0.1:55555                    *:*     

当我发送 1000 个数据包时,我得到:

$ ./sendto 
sent 14000 bytes

Recv-Q:

$ ss -ul|grep 55555
UNCONN     213504 0               127.0.0.1:55555                    *:*       

你的服务器(在 ctrl-c 之后):

$ ./recvfrom 55555
^C
278 UDP messages received

顺便说一句,213504/768 = 278。通过快速实验,我无法弄清楚要调整什么设置,以增加缓冲量。另外,我不知道为什么收到的数据包在这个队列中占用了这么多空间。可能有很多元数据?和你的 osX 一样,丢弃的数据包会显示在 netstat -su 中。

编辑: 使用 ss -ulm 进行额外观察,更详细地打印“套接字内存使用情况”:

UNCONN     213504 0               127.0.0.1:55555                    *:*       
skmem:(r213504,rb212992,t0,tb212992,f3584,w0,o0,bl0)

缓冲的 213504 字节比 rb 值高 512 字节。可能不是巧合,但需要阅读内核源代码才能找到。

您是否检查过 osX 上一个 UDP 数据报占用了多少?

编辑 2: 这仍然不是 osX 的合适答案,但在 Linux 上,我发现增加为接收缓冲区分配的内核内存允许我缓冲所有发送的 1000 个数据包。

有点矫枉过正,但我​​使用了这些(免责声明)随机调整缓冲区值可能会严重扰乱您的网络和内核):

net.core.rmem_max=1048568
net.core.rmem_default=1048568

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    • 2015-12-16
    • 1970-01-01
    • 2012-07-12
    • 2017-05-12
    • 2010-11-11
    相关资源
    最近更新 更多