2010-08-31 41 views
2

我使用使用UDP多播的boost.Asio创建了一个应用程序。 我不认为这个问题真的是特定于boost.Asio,而是一般的套接字编程,因为boost.Asio的网络设施大部分是套接字函数的封装。Windows-Linux-Mac上的UDP套接字网络断开行为

我构建的应用程序基于组播的例子( http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/multicast/receiver.cpp 和〜/ sender.cpp) 和我部署它在Windows,Linux上运行几台机器和 的Mac OSX与豹。我很高兴在所有的 平台上使用来自示例的代码进行多播。

我遇到问题的地方是当我断开网线时。当然,断开电缆总是会导致问题;)但是有些微妙的差别让我很疯狂。

我的测试设置总是如下:一台机器运行一个发送器,并接收一个接收器,看看同一台机器是否接收到自己的多播,另一台机器只运行接收器。我将 上的网线连接到运行发送方和接收方的机器上。

观察到的行为:

-Obviously,其中接收器运行没有收到任何 消息的计算机。这是可以预料的;)

- 当网络电缆被拔出的机器运行窗口时,发送器继续发送,同一机器上的接收器继续接收 。没有检测到错误。看来windows有一个固有的 回退回环?

- 当网络电缆拔出的机器运行Mac OSX时, 发送方继续发送,但没有显示错误消息,但同一台机器上的接收方不再接收。在您询问之前,我选择 不设置禁用环回选项。

- 当网络电缆拔出的机器运行Linux时, 发送器失败,并出现boost :: error“Network is unreachable”错误。显然, 因为发送者不能发送数据,所以接收者不再收到 任何东西。

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

对于Mac OSX我无法知道何时网络变得无法到达 以保持本地计算机上的接收器服务。

我观察到,在Mac OSX上,当网络电缆重新插入并且DHCP尚未获得新IP地址时,我得到一个“Network is unreachable”错误 。

所以基本上:我怎样才能做到这一点MacOSX上的本地客户端可以 还是从本地发件人收到?无论是通过检测网络损失 就像我在Linux上做的还是通过欺骗成行为像Windows。

通过谁拥有更深入地了解网络编程 比我人任何建议,非常感谢。

回答

0

我想在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 &路由。

1

当我遇到过这个问题,我的解决办法是安排在网络配置发生了变化,从OS得到通知。当我的程序收到该通知,它会等待几秒钟(以希望确保网络配置完成后改变),然后推倒并重建其所有的插座。这是一个痛苦,但它似乎工作得很好。

当然,没有操作系统无关的方式(即我所知道的)从OS获得通知时,网络配置发生了变化,所以我必须以不同的方式实现它的每个操作系统下。

为MacOS/X,我生成一个单独的手表的网络系配置线程,这看起来像这样:

#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下网络配置改变的通知(使用插座(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE)))和Windows(使用NotifyAddrChange())当量(也相当晦涩)机制,以我可以发布,如果他们会有所帮助,但我不想发送垃圾邮件如果你只对MacOS/X解决方案感兴趣,这个页面太多了。

+0

嗨,你可以上传你的代码?我非常需要他们在我的应用程序。谢谢 – kuchi 2012-03-18 09:27:25