根据最近的实证结果,并基于网络上的各种帖子,似乎在启用个人热点的iPhone上运行的应用程序无法将广播和/或多播发送到个人热点网络。任何人都可以解释这个问题的原因吗?
应用
我有一个IOS应用程序,使用跨平台C ++代码构建,可以在其运行的网络上广播和多播其存在。当iPhone连接到Wi-Fi网络时,该应用程序可以正常运行。在这种情况下,网络上的其他设备接收广播/多播,一切正常。通过将运行WireShark的计算机连接到网络,可以轻松验证这一点 - 可以在数据包跟踪中看到广播/多播数据包。
毋庸置疑,该应用程序在连接到本地Wi-Fi的iPhone上运行良好。
问题
当我在启用了个人热点的iPhone上运行应用程序时,不会在热点网络上发布广播/多播。这可以使用WireShark进行验证,WireShark在其跟踪中没有显示此类数据包。
使用个人热点作为能够处理广播和多播的网络路由器是否有任何限制?
当我在我的" WireSharking"上请求网页时使用浏览器的设备,个人热点正确响应所有数据包,返回Web内容。
附属信息
我遇到过报告相同或类似问题的其他Stack Overflow帖子:
在iPhone上编写这样的广播/多播应用程序的好教程是Michael Tyson" The Making of Talkie: Multi-interface broadcasting and multicast"。可以说我的应用程序符合所有要求(例如,在适当的位置设置套接字选项SO_BROADCAST,SO_DONTROUTE和IP_MULTICAST_IF)。
上面对参考文献(1)的回复写了" 可能是因为个人热点引入了网络地址转换?"。我过滤了WireShark跟踪以仅显示连接到热点IP的数据包,并且没有证据表明个人热点将任何内容发送到NAT地址。
总结
任何人都可以解释为什么运行个人热点的iPhone不会广播/组播数据包,以及如何解决这个问题?
非常感谢提前。
答案 0 :(得分:6)
我在广播中工作(在iOS 10上)
我发布了第二个答案,因为我找到了一种在个人热点模式下在iOS 10上播放广播的方法。解决方案有点复杂,但这就是我如何使用它:
getifaddrs(if)
遍历所有接口(do {} while (if = if->ifa_next)
)if->ifa_flags & IFF_LOOPBACK
)if->ifa_flags & IFF_BROADCAST
)int sock = socket()
int yes = 1; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes)
connect(sock, if->ifa_dstaddr, if->ifa_dstaddr->sa_len)
send()
发送您的消息!我已经确认,当iPhone连接到WiFi网络时,以及当它处于个人热点模式时,这都有效。
完整示例代码
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
-(IBAction)sayHello:(id)sender {
// fetch a linked list of all network interfaces
struct ifaddrs *interfaces;
if (getifaddrs(&interfaces) == -1) {
NSLog(@"getifaddrs() failed: %s", strerror(errno));
return;
}
// loop through the linked list
for(struct ifaddrs *interface=interfaces; interface; interface=interface->ifa_next) {
// ignore loopback interfaces
if (interface->ifa_flags & IFF_LOOPBACK) continue;
// ignore interfaces that don't have a broadcast address
if (!(interface->ifa_flags & IFF_BROADCAST) || interface->ifa_dstaddr == NULL) continue;
// check the type of the address (IPv4, IPv6)
int protocol_family;
struct sockaddr_in ipv4_addr = {0};
struct sockaddr_in6 ipv6_addr = {0};
struct sockaddr *addr;
if (interface->ifa_dstaddr->sa_family == AF_INET) {
if (interface->ifa_dstaddr->sa_len > sizeof ipv4_addr) {
NSLog(@"Address too big");
continue;
}
protocol_family = PF_INET;
memcpy(&ipv4_addr, interface->ifa_dstaddr, interface->ifa_dstaddr->sa_len);
ipv4_addr.sin_port = htons(16000);
addr = (struct sockaddr *)&ipv4_addr;
char text_addr[255] = {0};
inet_ntop(AF_INET, &ipv4_addr.sin_addr, text_addr, sizeof text_addr);
NSLog(@"Sending message to %s:%d", text_addr, ntohs(ipv4_addr.sin_port));
}
else if (interface->ifa_dstaddr->sa_family == AF_INET6) {
if (interface->ifa_dstaddr->sa_len > sizeof ipv6_addr) {
NSLog(@"Address too big");
continue;
}
protocol_family = PF_INET6;
memcpy(&ipv6_addr, interface->ifa_dstaddr, interface->ifa_dstaddr->sa_len);
ipv6_addr.sin6_port = htons(16000);
addr = (struct sockaddr *)&ipv6_addr;
char text_addr[255] = {0};
inet_ntop(AF_INET6, &ipv6_addr.sin6_addr, text_addr, sizeof text_addr);
NSLog(@"Sending message to %s:%d", text_addr, ntohs(ipv6_addr.sin6_port));
}
else {
NSLog(@"Unsupported protocol: %d", interface->ifa_dstaddr->sa_family);
continue;
}
// create a socket
int sock = socket(protocol_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == -1) {
NSLog(@"socket() failed: %s", strerror(errno));
continue;
}
// configure the socket for broadcast mode
int yes = 1;
if (-1 == setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes)) {
NSLog(@"setsockopt() failed: %s", strerror(errno));
}
// create some bytes to send
NSString *message = @"Hello world!\n";
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
if (-1 == connect(sock, addr, addr->sa_len)) {
NSLog(@"connect() failed: %s", strerror(errno));
}
// send the message
ssize_t sent_bytes = send(sock, data.bytes, data.length, 0);
if (sent_bytes == -1) {
NSLog(@"send() failed: %s", strerror(errno));
}
else if (sent_bytes<data.length) {
NSLog(@"send() sent only %d of %d bytes", (int)sent_bytes, (int)data.length);
}
// close the socket! important! there is only a finite number of file descriptors available!
if (-1 == close(sock)) {
NSLog(@"close() failed: %s", strerror(errno));
}
}
freeifaddrs(interfaces);
}
此示例方法在端口16000上广播UDP消息。
要进行调试,您可以使用工具socat
。只需在您的计算机上运行以下命令(该程序应与手机位于同一网络上):
socat UDP-RECV:16000 STDOUT
答案 1 :(得分:3)
快速解决方法
在开发使用UDP多播消息来发现网络设备的iPhone应用程序时,我也遇到了同样的问题。显然,iPhone会以个人热点模式阻止多播消息。
但是,iPhone似乎将172.20.10.0/28
子网用于个人热点网络上的设备。这意味着只有16个可能的地址。其中,172.20.10.0
显然未被使用,172.20.10.1
是iPhone本身,向172.20.10.15
发送消息的行为失败,并且“不允许”&#39;}错误。这意味着客户只能使用以下13个地址:172.20.10.2
,172.20.10.3
,...,172.20.10.14
。
所以我的解决方法非常简单:我只将广播消息发送到224.0.0.0
,而且还将它们发送到子网中的所有其他可能地址(172.20.10.2
- {{1} })。
当然,为了在生产应用程序中面向未来,您应该检查网络接口列表,检查IP和子网等,但是对于我个人使用这种方法就足够了。