【发布时间】:2021-07-01 15:29:15
【问题描述】:
我正在编写一个应用程序,它应该能够在套接字上接收 IPv4 或 IPv6 多播数据报。我编写了一个函数,可以通过setsockopt 接收套接字的多播数据报(见下面的代码)。我遇到的一个奇怪问题是 IPv4 案例 IP_ADD_MEMBERSHIP 的 setsockopt 有时会因 errno No such device 而失败,而其他时候它会按预期工作。我的应用程序在带有 raspbian 的树莓派上运行。非常感谢您的建议!
void setRecvMulticastAddr(int *socketFD, struct sockaddr *addr, char *interface, char *multicastaddr){
// Cast the sockaddr to a sockaddr storage struct
struct sockaddr_storage *addrStorage = (struct sockaddr_storage *) addr;
// Check if it is an IPv4 or IPv6 socket
if(addrStorage->ss_family == AF_INET){
// IPv4 multicast request
struct ip_mreq mreq;
// Convert the multicast IPv4 address string to an in_addr
struct in_addr multiaddr;
if(inet_pton(AF_INET, multicastaddr, &multiaddr) != 1){
printf("Could not convert the IPv4 multicast address: %s", multicastaddr);
exit(ERR_INETPTON_FAILED);
}
// Cast the sockaddr to a sockaddr_in struct
struct sockaddr_in *addrin = (struct sockaddr_in *) addrStorage;
// Fill out the IPv4 multicast request
mreq.imr_interface = addrin->sin_addr;
mreq.imr_multiaddr = multiaddr;
// ### The setsockopt that fails sometimes: ###
// Set the sockopt so the socket can receive on the multicast address
if(setsockopt(*socketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0){
perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
exit(ERR_SETSOCKOPT_FAILED);
}
}else{
// IPv6 multicast request
struct ipv6_mreq mreq;
// Set the interace name in the ifr
struct ifreq ifr;
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);
// Get the ifrindex based on the interface name
if(ioctl(*socketFD, SIOGIFINDEX, &ifr) < 0){
printf("Could not get ifrindex: %s\n", strerror(errno));
}
// Convert the multicast addrress string to an in_addr6
struct in6_addr multiaddr;
if(inet_pton(AF_INET6, multicastaddr, &multiaddr) != 1){
printf("Could not convert the IPv6 multicast address: %s", multicastaddr);
exit(ERR_INETPTON_FAILED);
}
// Fill out the IPv6 multicast request
mreq.ipv6mr_interface = ifr.ifr_ifindex;
mreq.ipv6mr_multiaddr = multiaddr;
// Set the sockopt so the socket can receive on the multicast address
if(setsockopt(*socketFD, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) != 0){
perror("Error setsockopt IPV6_JOIN_GROUP failed");
exit(ERR_SETSOCKOPT_FAILED);
}
}
return;
}
【问题讨论】:
-
尝试打印
addrin->sin_addr的值并验证它是否设置为活动本地接口上的活动 IP 地址。 -
@dbush 我正在设置套接字,该套接字应该能够接收带有 getaddrinfo 和提示中的 AI_PASSIVE 标志的多播数据报消息,并将其传递到 addr 参数中。我打印了值并按预期得到了
sin_addr is: 0.0.0.0。这一次,它在第一次尝试时就如您所愿。 -
使用 0.0.0.0 作为接口地址会导致系统加入“默认”接口。可能是该接口已关闭。如果您在主机上运行虚拟机或 docker,它可能会选择其中一个接口。尝试加入特定界面。
-
@dbush 我再次阅读了manpage,我认为我应该使用
ip_mreqn结构,因为ip_mreq是用于此目的的旧结构。它应该可以工作,但最好保存。也许我还应该考虑使用 ioctl 找出接口的本地 IP 地址,而不是使用0.0.0.0上的套接字的 sockaddr。 -
@dbush 非常感谢您的宝贵意见!我认为使用
0.0.0.0作为接口可能会导致问题。我记得有一次套接字在接口wlan0上加入了多播组,而不是像我想要的那样eth0。这也可以解释为什么它在 IPv6 上工作,因为它的参数只依赖于接口名称,因此它总是连接到正确的接口上。我明天试试。非常感谢!
标签: c sockets multicast ipv4 setsockopt