【问题标题】:Network broadcast/multicast not sent by iPhone in personal hotspot modeiPhone在个人热点模式下不发送网络广播/多播
【发布时间】:2015-03-01 16:13:40
【问题描述】:

根据最近的实证研究结果和网络上的各种帖子,在启用了个人热点的 iPhone 上运行的应用程序似乎无法将广播和/或多播发送到个人热点的网络上。任何人都可以阐明这个问题的原因吗?

应用程序

我有一个 IOS 应用程序,使用跨平台 C++ 代码构建,它可以将其存在广播和多播到它运行的网络上。当 iPhone 连接到 Wi-Fi 网络时,该应用程序可以完美运行。在这种情况下,网络上的其他设备接收广播/多播,并且一切正常。这可以通过将运行 WireShark 的计算机连接到网络来轻松验证——广播/多播数据包可以在数据包跟踪中看到。

不用说,该应用程序在连接到本地 Wi-Fi 的 iPhone 上运行良好。

问题

当我在启用了个人热点的 iPhone 上运行该应用程序时,没有广播/多播被发布到热点网络上。这可以使用 WireShark 进行验证,它在其跟踪中显示没有此类数据包。

将个人热点用作能够处理广播和多播的网络路由器是否有任何限制?

当我使用浏览器在“WireSharking”设备上请求网页时,个人热点正确响应所有数据包,返回网页内容。

抵押信息

我遇到过报告相同或类似问题的其他 Stack Overflow 帖子:

  1. TCP connection not working properly when using iPhone as hotspot
  2. Fail to send ssdp broadcast by personal hotspot

在 iPhone 上编写这种广播/多播应用程序的一个很好的教程是 Michael Tyson 的“The Making of Talkie: Multi-interface broadcasting and multicast”。只要说我的应用程序符合所有要求就足够了(例如,在适当的情况下设置套接字选项 SO_BROADCAST、SO_DONTROUTE 和 IP_MULTICAST_IF)。

上面对参考(1)的回复写道“可能是因为个人热点引入了网络地址转换?”。我过滤了 WireShark 跟踪以仅显示连接到热点 IP 的数据包,并且没有证据表明个人热点向 NAT 地址发送任何内容。

总结

谁能解释为什么运行个人热点的 iPhone 不广播/多播数据包,以及如何解决这个问题?

非常感谢。

【问题讨论】:

  • 有趣 - 但并不奇怪。 Apple 非常喜欢直接锁定内容,这听起来就像这里发生的事情(尽管我没有个人热点会话期间的消息传递经验)。你可能会发现没有什么可以做的。我唯一的建议是看看 Bonjour 功能是否正确,这可能有助于了解正在发生的事情。如果它有效,也许你可以在这种情况下使用 Bonjour + 单播,如果它对你特别重要的话。
  • @Michael:非常感谢您的回复。我担心我可能不得不编写一个特定于 IOS 的修复程序(即使用 Bonjour),而这适用于当前使用共享 C++ 代码的跨平台产品。我一定会调查那个角度。您的观点说得很好——如果 Bonjour 工作正常,Apple 将限制非 Bonjour 应用程序的热点功能。
  • 不用担心 - 这里有一个小小的 bonjour 测试应用程序,您也可以用它来试用。只需在运行个人热点的手机上运行一个实例,然后在另一台设备上运行另一个实例。 dl.dropboxusercontent.com/u/6956432/BonjourTest.zip
  • 非常感谢!我会测试并报告结果(我女儿今天要结婚所以可能需要几天时间)。
  • 我也遇到了同样的问题,请问有更新吗?

标签: c++ ios wifi personal-hotspot


【解决方案1】:

我的广播工作正常(在 iOS 10 上)

我发布了第二个答案,因为我找到了一种在个人热点模式下在 iOS 10 上进行广播的方法。解决方案有点复杂,但我是这样工作的:

  1. 使用getifaddrs(if)循环所有接口(do {} while (if = if->ifa_next))
  2. 拒绝环回接口 (if->ifa_flags & IFF_LOOPBACK)
  3. 仅过滤支持广播的接口 (if->ifa_flags & IFF_BROADCAST)
  4. int sock = socket()创建一个socket
  5. 启用广播:int yes = 1; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes)
  6. 连接广播地址:connect(sock, if->ifa_dstaddr, if->ifa_dstaddr->sa_len)
  7. 现在使用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

【讨论】:

  • 非常感谢您抽出宝贵时间记录该问题的两种不同解决方案。我相信其他人会从您的解释中受益。
  • 嗨,我一直在寻找这样的解决方案,但我不知道 C 以及它如何适合客观的 c 方法。看到它和你之前的解释将帮助我更好地理解这一点。谢谢。
  • @MihaiFischer 我用完整的示例代码更新了我的答案
  • @JakobEgger 在此期间我设法做了类似的事情,但现在我对整体情况有了更好的了解。谢谢!
【解决方案2】:

快速而肮脏的解决方法

在开发使用 UDP 多播消息来发现网络上的设备的 iPhone 应用程序时,我也遇到了同样的问题。显然 iPhone 在个人热点模式下会阻止多播消息。

但是,iPhone 似乎将172.20.10.0/28 子网用于个人热点网络上的设备。这意味着只有 16 个可能的地址。其中,172.20.10.0 显然没有使用,172.20.10.1 是 iPhone 本身,向172.20.10.15 发送消息失败并出现“不允许”错误。这意味着客户端只能使用以下 13 个地址:172.20.10.2172.20.10.3、...、172.20.10.14

所以我的解决方法非常简单:我不仅将广播消息发送到224.0.0.0,还将它们发送到子网中所有其他可能的地址 (172.20.10.2 - 172.20.10.14)。

当然,为了在生产应用中面向未来,您可能应该检查网络接口列表、检查 IP 和子网等,但对于我个人而言,这种方法就足够了。

【讨论】:

    猜你喜欢
    • 2011-09-26
    • 2013-06-13
    • 1970-01-01
    • 2011-03-26
    • 2015-07-31
    • 1970-01-01
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多