【问题标题】:User buffer size to receive multicast packets?接收多播数据包的用户缓冲区大小?
【发布时间】:2019-10-25 11:51:22
【问题描述】:

以下代码来自 Git。它加入一个多播组并接收数据包。

这里我们循环并在一个名为msgbuf的缓冲区中接收数据:

while (1) 
{
    char msgbuf[MSGBUFSIZE];
    const int addrlen = sizeof(addr);
    const int nbytes = recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, &addrlen);

我如何选择缓冲区msgBuf 的大小? 是否必须是最大数据包大小?还是在处理第一个数据包时需要存储多个数据包?

完整代码:

int main(int argc, char *argv[])
{
    if (argc != 3) {
       printf("Command line args should be multicast group and port\n");
       printf("(e.g. for SSDP, `listener 239.255.255.250 1900`)\n");
       return 1;
    }

    char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
    int port = atoi(argv[2]); // 0 if error, which is an invalid port

    if(port <= 0)
    {
        perror("Invalid port");
        return 1;
    }


    // create what looks like an ordinary UDP socket
    //
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) 
    {
        perror("socket");
        return 1;
    }

    // allow multiple sockets to use the same PORT number
    //
    u_int yes = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof(yes)) < 0)
    {
       perror("Reusing ADDR failed");
       return 1;
    }

        // set up destination address
    //
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender
    addr.sin_port = htons(port);

    // bind to receive address
    //
    if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) 
    {
        perror("bind");
        return 1;
    }

    // use setsockopt() to request that the kernel join a multicast group
    //
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(group);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt");
        return 1;
    }

    // now just enter a read-print loop
    //
    while (1) 
    {
        char msgbuf[MSGBUFSIZE];
        const int addrlen = sizeof(addr);
        const int nbytes = recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, &addrlen);

        if (nbytes < 0) 
        {
            perror("recvfrom");
            return 1;
        }
        msgbuf[nbytes] = '\0';
        puts(msgbuf);
     }

    return 0;
}

【问题讨论】:

  • 如果recvfrom() 读取MSGBUFSIZE 字节,代码会导致UB。然后msgbuf[nbytes] = '\0': 导致缓冲区溢出。如果那是生产代码,那么实际上包似乎更小
  • @IngoLeonhardt 我很欣赏你的观点,但问题是关于如何调整缓冲区的大小,所以说有溢出有点毫无意义。通常,我需要知道如何确定缓冲区大小。它是基于最大的数据包大小吗?或者,我是否必须存储多个数据包,从而使缓冲区变大?
  • 我知道这有点过时了。你的问题的答案当然取决于具体的沟通,我不知道。但至少缓冲区溢出可能表明应该不需要在该通信中存储多个数据包,因为我认为在这种情况下会发生溢出
  • @IngoLeonhardt 不用担心,我并不是要听起来粗鲁,我只是想重定向到它需要存储一个数据包还是多个数据包的问题。我认为它只是一个数据包(操作系统缓冲区可能存储任何累积?)
  • 我从来没有觉得你很粗鲁 :-) 如果我最后的评论给你的印象是因为我不是母语人士。希望您的问题能引起了解更多的人的兴趣

标签: c sockets udp multicast berkeley-sockets


【解决方案1】:

与将数据包组合成流的 TCP 不同,UDP 尊重数据包边界,因此recvfrom 一次只能获取一个数据包。

所以MSGBUFSIZE 只需要和单个数据包一样大。如果您不使用巨型数据包,则为 1500,否则为 9000。

【讨论】:

    【解决方案2】:

    正如@Ingo 所说,您应该在这段代码中使用:

        char msgbuf[MSGBUFSIZE + 1];
    

    + 1 是因为 recvfrom 最多可以将 MSGBUFSIZE 字节写入数组,然后您在最后写入另一个 NUL 字节。

    至于为MSGBUFSIZE 选择一个值,这将取决于协议规范。鉴于大多数物理网络很难在没有碎片的情况下发送超过 1500 个字节,因此 2048 之类的值可能是一个合理的值。您还可以检查nbytes == MSGBUFSIZE(也可以使用MSG_TRUNC)并报告“数据包被截断”警告,但这对于通过公共互联网路由的数据包基本上不会发生

    回应:

    在处理第一个数据包时是否需要存储多个数据包?

    你通常会让网络堆栈来处理这个问题。 recv 维护数据包/数据报边界,因此将始终在提供的缓冲区地址处开始写入下一个数据包。同样,这取决于您如何检测和处理错误的协议,例如丢失或乱序的数据包,以及超时

    【讨论】:

      猜你喜欢
      • 2021-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-05
      相关资源
      最近更新 更多