我读到的所有内容都告诉我,UDP组播是在IPv6中执行此操作的正确方法。我找到了一些示例代码,但它不适用于我尝试过的任何版本的iOS或Mac OS X.
这个代码是从我们程序中的C / C ++ lib调用的 - 很难回调到Swift,Obj-C,Java等。这个代码将由我们的Mac OS X和Android版本共享应用程序。有人会认为在任何POSIX环境中都可以在C中进行IPv6组播!
在下面的示例中,执行成功直到最后的sendto()调用,该调用实际上发送UDP消息。 sendto()失败,失败后errno设置为EBROKENPIPE(22)。
我最好的猜测是我错过了一些必需的setsockopt()调用,或者使用了错误的多播地址。现在,我很难过。
这是我正在进行的函数调用(在UDP端口4031上组播“是否有任何人?”):
char *msg = "Is anybody out there?";
err = multicast_udp_msg ( "FF01::1111", 4031, msg, strlen(msg) );
这是被调用的代码:
// Multicasts a message on a specific UDP port.
// myhost - IPv6 address on which to multicast the message (i.e., ourself)
// port - UDP port on which to broadcast the mssage
// msg - message contents to broadcast
// msgsize - length of message in bytes
// Return value is zero if successful, or nonzero on error.
int multicast_udp_msg ( char *myhost, short port, char *msg, size_t msgsize )
{
int sockfd, n;
char service[16] = { 0 };
int err = 0;
struct addrinfo hints = { 0 }, *res, *ressave;
struct sockaddr_storage addr = { 0 };
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
sprintf ( service, "%hd", port );
n = getaddrinfo ( myhost, service, &hints, &res );
if ( n < 0 )
{
fprintf(stderr, "getaddrinfo error:: [%s]\n", gai_strerror(n));
return -1;
}
ressave = res;
sockfd = socket ( res->ai_family, res->ai_socktype, res->ai_protocol );
if ( sockfd >= 0 )
{
memcpy ( &addr, res->ai_addr, sizeof ( addr ) );
if ( joinGroup ( sockfd, 0, 8, &addr ) == 0 )
if ( bind ( sockfd, res->ai_addr, res->ai_addrlen ) == 0 )
if ( sendto ( sockfd, msg, msgsize, 0, (struct sockaddr *) &addr, sizeof ( addr ) ) < 0 )
err = errno;
close ( sockfd );
res = res->ai_next;
}
freeaddrinfo ( ressave );
return err;
}
int
joinGroup(int sockfd, int loopBack, int mcastTTL,
struct sockaddr_storage *addr)
{
int r1, r2, r3, retval;
retval=-1;
switch (addr->ss_family) {
case AF_INET: {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=
((struct sockaddr_in *)addr)->sin_addr.s_addr;
mreq.imr_interface.s_addr= INADDR_ANY;
r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
&loopBack, sizeof(loopBack));
if (r1<0)
perror("joinGroup:: IP_MULTICAST_LOOP:: ");
r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
&mcastTTL, sizeof(mcastTTL));
if (r2<0)
perror("joinGroup:: IP_MULTICAST_TTL:: ");
r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&mreq, sizeof(mreq));
if (r3<0)
perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");
} break;
case AF_INET6: {
struct ipv6_mreq mreq6;
memcpy(&mreq6.ipv6mr_multiaddr,
&(((struct sockaddr_in6 *)addr)->sin6_addr),
sizeof(struct in6_addr));
mreq6.ipv6mr_interface= 0; // cualquier interfaz
r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loopBack, sizeof(loopBack));
if (r1<0)
perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");
r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&mcastTTL, sizeof(mcastTTL));
if (r2<0)
perror("joinGroup:: IPV6_MULTICAST_HOPS:: ");
r3= setsockopt(sockfd, IPPROTO_IPV6,
IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
if (r3<0)
perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");
} break;
default:
r1=r2=r3=-1;
}
if ((r1>=0) && (r2>=0) && (r3>=0))
retval=0;
return retval;
}
欢迎思考!
-Tim
答案 0 :(得分:6)
经过Apple的一些反复和一些额外的背景,我们得到了答案。但这不是我原来问题的答案。首先,这是上下文的Apple线程:
https://forums.developer.apple.com/message/71107
事实证明,IPv6组播实际上并不是我们解决手头真正问题所需要的 - 即在本地Wi-Fi网络上寻找传统的嵌入式设备。我们真的不得不使用IPv4 UDP广播来做到这一点。我们的嵌入式设备忽略了IPv6组播数据包,例如地球忽略了飞过它的中微子。
Apple给了我们一个setsockopt()调用,它使IPv4 UDP广播能够在基础设施Wi-Fi网络上的iOS 9中运行。这是此功能的预期用例。当广播无法在Ad Hoc Wi-Fi网络中运行时(这似乎是已知的iOS 9问题),Apple也给我们带来了失败的可能原因。所以,虽然这里没有回答我原来的问题,但基本问题已经解决了。