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

时间:2019-10-25 11:51:22

标签: c sockets udp multicast berkeley-sockets

以下代码来自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;
}

2 个答案:

答案 0 :(得分:2)

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

因此MSGBUFSIZE仅需要与单个数据包一样大。如果您使用的不是巨型数据包,则为1500,否则为9000。

答案 1 :(得分:1)

@Ingo指出,在此代码中,您应该使用:

    char msgbuf[MSGBUFSIZE + 1];

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

就为MSGBUFSIZE选择一个值,这将取决于协议规范。考虑到大多数物理网络将很难发送超过1500个字节而不会产生碎片,例如2048可能是一个合理的值。您还可以检查nbytes == MSGBUFSIZE(也许也使用MSG_TRUNC)并报告“数据包被截断”警告,但是对于通过公用Internet路由的数据包基本上不会发生

响应:

  

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

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