3

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

2 回答 2

2

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

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

于 2019-10-25T17:54:42.057 回答
1

正如@Ingo 所指出的,在这段代码中你应该使用:

    char msgbuf[MSGBUFSIZE + 1];

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

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

回应:

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

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

于 2019-10-25T17:24:59.793 回答