在 C++ 中的同一套接字上接收单播和多播

Receiving Unicast and Multicast on the Same Socket in C++

我需要在特定 IP 的同一套接字和端口上接收单播和多播 UDP。根据此处提出的其他问题,这应该是可能的。我可以设置所有选项并成功绑定,但我无法接收任何流量。它只是在 read() 调用时保持阻塞状态。

我尝试了很多不同的选项,例如 SO_BROADCASTSO_REUSEADDRSO_REUSEPORT。我曾尝试将多播组地址设置为“0.0.0.0”,但失败了。我只是不知道如何正确地做到这一点。

这是我当前状态的精简版:

struct sockaddr_in localSocket;

int sd = socket(AF_INET, SOCK_DGRAM, 0);
//error check here

int broadcast = 1;
//I have tried either one of these options, as well as SO_BROADCAST. 
// Now I am trying to use both
if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char*)&broadcast, sizeof(broadcast)) < 0 || 
    (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, (char*)&broadcast, sizeof(broadcast)) < 0)) 
    //error handling here
else 
{
    memset((char*) &locakSocket, 0, sizeof(localSocket));
    localSocket.sin_family = AF_INET;
    //port that I want to receive on
    localSocket.sin_port = htons(port);
    //local IP that I want to receive on. I have tried "0.0.0.0" here as well, 
    //but I still do not receive anything
    localSocket.sin_addr.s_addr = inet_addr(IP); 
}

if(bind(sd,(struc sockaddr*)&kicakSicjetm suzeif(localSocket))<0)
    //error handling
else
{
    //trying to join multicast here:
    struct ip_mreq group;
    //IP address that the multicast is being sent from. 
    // I have tried "0.0.0.0" here too, but that fails.
    group.imr_multiaddr.s_addr = inet_addr("239.255.1.1");
    //local IP that I want to receive on. 
    I have tried skipping this line, but I still do not receive any traffic
    group.imr_interface.s_addr = inet_addr(IP);
}

if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)) < 0)
    //error handling
else
{
    while(!terminate)
    {
        //this breakpoint is hit, but I never hit any line afterwards
        numbytes = read(sd,packet,sizeof(packet));
    }
}

自从我到达最后一行后,我知道 setsocketopt()bind() 等的 none 失败了。我只是什么都不读。 我做错了什么吗? setsockoptbindsetsockopt 感觉很奇怪,但这就是我的研究引导我的地方。

编辑:这是针对 linux 系统的。 有两个问题:
if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char*)&broadcast, sizeof(broadcast)) < 0 || (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, (char*)&broadcast, sizeof(broadcast)) < 0))

localSocket.sin_addr.s_addr = inet_addr(IP);

我只是简单地注释掉了一对setsockopt,以至于我直到接近尾声才setsockopt。我使用了不正确的套接字选项,例如 SO_REUSEADDRSO_REUSEPORT。此外,我绑定到我想接收的本地 IP,而不是绑定到 IPADDR_ANY。感谢大家的帮助

假设您正在使用 Linux,当在同一个套接字上接收单播和多播流量时,您 必须 绑定到 INADDR_ANY,即 0.0。 0.0。如果绑定到特定接口,则不会获得多播流量,如果绑定到多播地址,则不会获得单播流量。

所以像这样设置你的绑定地址:

memset((char*) &locakSocket, 0, sizeof(localSocket));
localSocket.sin_family = AF_INET;
localSocket.sin_port = htons(port);
localSocket.sin_addr.s_addr = INADDR_ANY;

您也不想使用SO_REUSEADDRSO_REUSEPORT。接收多播时,它确实允许您在同一个端口上有两个打开的套接字,这两个套接字都将接收多播流量,但是单播流量是去往两个,只去一个,还是轮询并没有明确定义。您也不需要 SO_BROADCAST 在您的接收器中,因为它只控制发送到广播地址的传出数据报(即 x.x.x.255)。

您加入多播组的方式看起来不错。请注意,如果您希望多播流量进入多个接口,那么您需要在每个接口上加入该组。

此外,如评论中所述,您应该使用 recvfrom 从 UDP 套接字接收数据。这允许您获取传入数据报的来源 IP/port,还允许您根据需要设置特定于套接字的标志。