【问题标题】:UDP socket network disconnect behavior on Windows-Linux-MacWindows-Linux-Mac 上的 UDP 套接字网络断开行为
【发布时间】:2010-08-31 21:54:17
【问题描述】:

我使用 boost.Asio 使用 UDP 多播创建了一个应用程序。 我不认为这个问题是针对 boost.Asio 的,而是针对一般的套接字编程,因为 boost.Asio 的网络设施主要是对套接字函数的封装。

我根据多播示例构建了应用程序( http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/multicast/receiver.cpp 和 ~/sender.cpp) 我将它部署在多台运行 Windows、Linux 和 Mac 与 OSX Leopard。我很高兴在所有人上进行多播 平台使用源自示例的代码开箱即用。

我遇到问题的地方是当我断开网络电缆时。的 当然,断开电缆总是会导致问题;)但是 是让我抓狂的细微差别。

我的测试设置始终如下:一台机器运行一个发送器,并且 接收者,查看同一台机器是否接收到自己的多播,以及 另一台机器只运行接收器。我拉上网线 运行发送方和接收方的机器。

观察到的行为:

-显然接收器运行的机器不再接收 消息。这是意料之中的;)

-拔掉网线的机器运行windows时, 发送者继续发送,同一台机器上的接收者继续发送 接受。未检测到错误。似乎windows有一个内在的 回退到环回?

-拔下网线的机器运行Mac OSX时, 发件人继续发送,不显示错误消息,但 同一台机器上的接收器不再接收。在你问之前,我 选中 NOT 以设置禁用环回选项。

-拔下网线的机器运行Linux时, 发件人因 boost::error “网络无法访问”而失败。明显地, 由于发送方无法发送数据,因此接收方不会收到 什么都没有了。

对于 Linux,我可以通过捕获 “无法访问”错误(或捕获错误的写入字节数)和 在我的代码中设置一个标志,随后将所有数据发送到 127.0.0.1 而不是多播地址。我会定期检查 send_to 是否在 多播端点仍然会产生错误以检测网络重新连接 并返回多播。这就像一个魅力,因为 接收者是 bind() 到 inaddr_any,因此也在 127.0.0.1 上侦听。

对于 Mac OSX,我无法注意到网络何时变为 无法在本地机器上为接收者提供服务。

我观察到在 Mac OSX 上我收到“网络无法访问”错误 重新插入网线且 DHCP 未启用时 尚未获得新的 IP 地址。

所以基本上:我怎样才能在 MacOSX 上实现本地客户端可以 仍然从本地发件人那里收到?通过检测网络丢失 就像我在 Linux 上所做的那样,或者通过欺骗它使其表现得像 Windows。

对网络编程有更深入了解的人的任何建议 比我有的,非常感谢。

【问题讨论】:

    标签: sockets network-programming udp boost-asio multicast


    【解决方案1】:

    当我遇到此问题时,我的解决方案是安排在网络配置发生更改时从操作系统获取通知。当我的程序收到该通知时,它会等待几秒钟(希望确保网络配置已完成更改),然后拆除并重建其所有套接字。这很痛苦,但似乎效果很好。

    当然,当网络配置发生变化时,没有与操作系统无关的方式(据我所知)从操作系统获取通知,因此我必须在每个操作系统下以不同的方式实现它。

    对于 MacOS/X,我生成了一个单独的 watch-the-network-config 线程,如下所示:

    #include <SystemConfiguration/SystemConfiguration.h>
    
    void MyNetworkThreadWatcherFunc(void *)
    {
       SCDynamicStoreRef storeRef = NULL;
       CFRunLoopSourceRef sourceRef = NULL;
       if (CreateIPAddressListChangeCallbackSCF(IPConfigChangedCallback, this, &storeRef, &sourceRef) == noErr)
       {
          CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
    
          while(_threadKeepGoing)   // may be set to false by main thread at shutdown time
          {
             CFRunLoopRun();
          }
    
          // cleanup time:  release our resources
          CFRunLoopRemoveSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
          CFRelease(storeRef);
          CFRelease(sourceRef);
        }
     }
    

    还有这个设置/支持代码,从上面的函数调用:

    static OSStatus MoreSCError(const void *value) {return MoreSCErrorBoolean(value != NULL);}
    static OSStatus CFQError(CFTypeRef cf) {return (cf == NULL) ? -1 : noErr;}
    static void CFQRelease(CFTypeRef cf) {if (cf != NULL) CFRelease(cf);}
    
    // Create a SCF dynamic store reference and a corresponding CFRunLoop source.  If you add the
    // run loop source to your run loop then the supplied callback function will be called when local IP
    // address list changes.
    static OSStatus CreateIPAddressListChangeCallbackSCF(SCDynamicStoreCallBack callback, void *contextPtr, SCDynamicStoreRef *storeRef, CFRunLoopSourceRef *sourceRef)
    {
       OSStatus                err;
       SCDynamicStoreContext   context = {0, NULL, NULL, NULL, NULL};
       SCDynamicStoreRef       ref = NULL;
       CFStringRef             patterns[2] = {NULL, NULL};
       CFArrayRef              patternList = NULL;
       CFRunLoopSourceRef      rls = NULL;
    
       // Create a connection to the dynamic store, then create
       // a search pattern that finds all entities.
       context.info = contextPtr;
       ref = SCDynamicStoreCreate(NULL, CFSTR("AddIPAddressListChangeCallbackSCF"), callback, &context);
       err = MoreSCError(ref);
       if (err == noErr)
       {
          // This pattern is "State:/Network/Service/[^/]+/IPv4".
          patterns[0] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
          err = MoreSCError(patterns[0]);
          if (err == noErr)
          {
             // This pattern is "State:/Network/Service/[^/]+/IPv6".
             patterns[1] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
             err = MoreSCError(patterns[1]);
          }
       }
    
       // Create a pattern list containing just one pattern,
       // then tell SCF that we want to watch changes in keys
       // that match that pattern list, then create our run loop
       // source.
       if (err == noErr)
       {
           patternList = CFArrayCreate(NULL, (const void **) patterns, 2, &kCFTypeArrayCallBacks);
           err = CFQError(patternList);
       }
       if (err == noErr) err = MoreSCErrorBoolean(SCDynamicStoreSetNotificationKeys(ref, NULL, patternList));
       if (err == noErr)
       {
           rls = SCDynamicStoreCreateRunLoopSource(NULL, ref, 0);
           err = MoreSCError(rls);
       }
    
       // Clean up.
       CFQRelease(patterns[0]);
       CFQRelease(patterns[1]);
       CFQRelease(patternList);
       if (err != noErr)
       {
          CFQRelease(ref);
          ref = NULL;
       }
       *storeRef = ref;
       *sourceRef = rls;
    
       return err;
    }
    
    
    static void IPConfigChangedCallback(SCDynamicStoreRef /*store*/, CFArrayRef /*changedKeys*/, void *info)
    {
       printf("Network config changed!  Place code here to send a notification to your main thread, telling him to close and recreate his sockets....\n");
    }
    

    在 Linux(使用 socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE))和 Windows(使用 NotifyAddrChange())下,我可以发布等效的(也相当模糊的)机制来获取网络配置更改通知,如果它们会很有帮助,但如果您只对 MacOS/X 解决方案感兴趣,我不想在此页面上过多地发送垃圾邮件。

    【讨论】:

    • 您好,您可以发布您拥有的那些代码吗?我在我的应用程序中非常需要它们。谢谢
    【解决方案2】:

    我认为在 Windows 中发生的情况是,即使您断开电缆,Windows 仍然保持以太网接口打开,因为您有一些套接字连接到它,并且您发送到的 multicast_address 保持有效。 Windows 也可能会更改发送方/接收方正在使用的接口,因此更改在套接字级别是透明的。

    我认为 OS X 中发生的情况是,当您断开电缆连接时,发送方多播到环回接口,但接收方仍连接到断开连接的以太网接口。也有可能 OS X 正在配置发送方发送到的自分配 IP,但接收方仍在侦听旧的 DHCP IP。

    在 Linux 中,当您断开电缆连接时,以太网接口会丢失其 IPv4 地址,删除到 239.255.0.1 的路由,环回接口未配置为在 127 之外发送任何内容。.。 *,所以你得到一个错误。

    也许解决方案是定期重新加入 OS X 接收器上的组? (也许您还必须定期重建发送者的端点。)

    要尝试的另一件事是在 OS X 上使用自分配 IP,这样当电缆连接或断开时,您拥有相同的 IP 和路由。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 2020-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-17
      • 2011-05-14
      相关资源
      最近更新 更多