C 在所有接口(interface)上监听多播,响应与接收到的相同

标签 c sockets multicast

我正在尝试在系统中的所有接口(interface)上收听多播,但仅在我收到多播数据包的接口(interface)上响应。

我所做的是为每个接口(interface)创建一个套接字,问题就从这里开始。

当我将接口(interface)绑定(bind)到 INADDR_ANY 时,它将接收所有接口(interface)的数据包并在默认接口(interface)上发送。如果我将端口绑定(bind)到特定接口(interface),它将不会接收多播数据包(但它能够在正确的接口(interface)上发送它)。

我已经尝试设置诸如 IP_ADD_MEMBERSHIPIP_MULTICAST_IF 之类的选项,但没有成功。

我认为其他选项应该是创建一个套接字来接收所有接口(interface)的所有 ifs 和发送方套接字,但在这种方法中我不知道进入哪个 ifs 数据包...

代码示例(经过简化,没有错误处理之类的东西):

创建套接字:

//here i am looping over all interfaces from getifaddrs
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;

sockets[i] = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

mreq.imr_multiaddr.s_addr=inet_addr(MDNS_ADDRESS);
mreq.imr_interface.s_addr=pAddr->sin_addr.s_addr;

setsockopt(sockets[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
setsockopt(sockets[i], IPPROTO_IP, IP_MULTICAST_IF, &pAddr, sizeof(struct in_addr));

memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; //or pAddr->sin_addr.s_addr;
my_addr.sin_port = htons(port);

bind(sockets[i], (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1);

接收和发送:

recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len);

//do some magic and response (response should be a multicast too)

destination.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);
destination.sin_family = AF_INET;
destination.sin_port = htons( port );
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);

我想在工作中创建与 mDNS 类似的东西,所以当数据包进入特定接口(interface)程序时,应该回答相同的 if 和一些关于这个 if 的数据。它不应该在其他 ifs 上发送它,因为它可能与它们无关,但它应该将它作为多播发送,这样同一网络中的任何其他主机都会收到响应。

最佳答案

你应该只需要一个 socket 。

首先绑定(bind)到 INADDR_ANY 和您选择的端口。然后在您要接收多播的每个接口(interface)上使用 IP_ADD_MEMBERSHIP 调用 setsockopt。最后,在要发送多播的接口(interface)上使用 IP_MULTICAST_IF 调用 setsockopt。确保在每次调用时检查错误。

int socket s;
struct sockaddr_in sin;
struct ip_mreq mreq;
struct in_addr out_addr;

bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=htonl(INADDR_ANY);
sin.sin_port = htons(1044);   // or whatever port you listen on

if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perror("socket failed");
    exit(1);
}

if (bind(s, (struct sockaddr *)&sin, sizeof(sin))==-1) {
    perror("bind failed");
    exit(1);
}

// Do this in a loop for each interface
mreq.imr_multiaddr = inet_addr("230.4.4.1");     // your multicast address
mreq.imr_interface = inet_addr("192.168.1.1");   // your incoming interface IP
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) {
    perror("Error joining multicast group");
    exit(1);
}

out_addr.s_addr = inet_addr("192.168.1.1");   // your outgoing interface IP
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,sizeof(out_addr))== -1) {
    perror("Error setting outgoing interface");
    exit(1);
}

使用多播时,您应该始终绑定(bind)到 INADDR_ANY 地址。否则会中断 Linux 系统上的多播。

关于C 在所有接口(interface)上监听多播,响应与接收到的相同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32278414/

相关文章:

c - 同一内存地址处的两个不同值 - 汇编

c# - SocketException : No such host is known

java - Java中通过socket传递压缩的JSON

c++ - WSAConnect() 与 ConnectEx()

udp - 无法加入多播组: No such Device

c - 使用 openmp 确保缓冲区访问是私有(private)的

c - 在 Raspberry Pi 上使用 Alsa 出现段错误

c - 信号量 System V - semop 实现

linux - 在 SuSe Linux 和 JDK7 上,加入多个组不适用于 Netty 3.6.5.Final

FFmpeg 使用 UDP 进行多播数据包过滤