接收多播数据包的用户缓冲区大小?

User buffer size to receive multicast packets?

以下代码来自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] = '[=11=]';
        puts(msgbuf);
     }

    return 0;
}

如@Ingo 所述,在此代码中您应该使用:

    char msgbuf[MSGBUFSIZE + 1];

+ 1是因为recvfrom最多可以向数组中写入MSGBUFSIZE个字节,然后你在末尾写入另一个NUL字节。

至于为 MSGBUFSIZE 选择一个值,这将取决于协议规范。考虑到大多数物理网络在没有碎片的情况下发送超过 1500 个字节将很困难,像 2048 这样的值可能是一个合理的值。您还可以检查 nbytes == MSGBUFSIZE(也可能使用 MSG_TRUNC)并报告 "packet truncated" 警告,但这对于通过 public 互联网路由的数据包基本上不会发生

回应:

do I need to store multiple packets whilst I process the first?

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

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

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