Windows-Linux-Mac上的UDP套接字网络断开连接行为 [英] UDP socket network disconnect behavior on Windows-Linux-Mac

查看:121
本文介绍了Windows-Linux-Mac上的UDP套接字网络断开连接行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I made an application using boost.Asio using UDP multicast. I don't think the question is really specific to boost.Asio but to sockets programming in general, since boost.Asio's network facilities are mostly wrappers to socket functions.

我基于多播示例构建了应用程序( http://www.boost. org/doc/libs/1_44_0/doc/html/boost_asio/example/multicast/receiver.cpp 和〜/sender.cpp) 我将其部署在运行Windows,Linux和Windows Server 2003的多台计算机上 装有OSX Leopard的Mac.我很高兴在所有 平台可使用从示例中获取的代码直接使用.

I constructed the application based on the multicast examples ( http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/multicast/receiver.cpp and ~/sender.cpp) and I deployed it on several machines running on Windows, Linux and a Mac with OSX Leopard. I'm very pleased that multicasting on all platforms works out of the box with the code derived from the examples.

我遇到问题的地方是断开网络电缆的连接.的 当然,断开电缆连接总是会造成问题;),但是 是让我发疯的微妙差异.

Where I run into problems, is when I disconnect the network cable. Of course, disconnecting the cable will always cause problems ;) but there are subtle differences that drive me crazy.

我的测试设置始终如下:一台运行发件人的计算机, 接收者,以查看同一台机器是否接收到自己的多播,以及 另一台仅运行接收器的机器.我拉上网络线 运行发送方和接收方的机器.

My testing setup is always as follows: One machine running a sender and a receiver, to see if the same machine receives its own multicast, and another machine running only the receiver. I pull the network cord on the machine running the sender and the receiver.

观察到的行为:

-显然,接收方运行的机器不再接收任何信息 消息.那是意料之中的;)

-Obviously the machine where the receiver runs doesn't receive any more messages. That was to be expected ;)

-拔下网络电缆的计算机运行Windows时, 发送方继续发送,同一台计算机上的接收方继续 接受.未检测到错误.看来窗户有一个内在的 回退到环回?

-When the machine where the network cable is unplugged runs windows, the sender continues to send and the receiver on the same machine continues to receive. No errors detected. It seems windows has an intrinsic fallback to loopback?

-拔下网络电缆的计算机运行Mac OSX时, 发件人继续发送但未显示任何错误消息,但是 同一台机器上的接收方不再接收.在你问之前,我 选中否"以设置禁用环回"选项.

-When the machine where the network cable is unplugged runs Mac OSX, the sender continues to send with no error message displayed, but the receiver on the same machine doesn't receive anymore. Before you ask, I checked NOT to set the disable loopback option.

-拔下网络电缆的计算机运行Linux时, 发件人失败,并出现boost :: error网络不可达".明显地, 由于发送方无法发送数据,因此接收方不会接收 一切了.

-When the machine where the network cable is unplugged runs Linux, the sender fails with a boost::error "Network is unreachable". Obviously, since the sender can't send the data, the receiver doesn't receive anything anymore.

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

For Linux, I can fake the behavior of Windows by catching the "unreachable" error (or catching a wrong number of bytes written) and setting a flag in my code, subsequently sending all data to 127.0.0.1 instead of the multicast address. I regularly check if a send_to on the multicast endpoint still yields an error to detect a network reconnect and go back to multicasting. This works like a charm because the receiver is bind() to inaddr_any and thus listens on 127.0.0.1 also.

对于Mac OSX,我无法注意到网络何时变为 无法保持本地计算机上的接收器服务.

For Mac OSX I have no means of noticing when the network becomes unreachable to keep up the service for the receiver on the local machine.

我观察到在Mac OSX上出现网络无法访问"错误 重新插入网络电缆且DHCP暂时没有时 尚未获得新的IP地址.

I observed that on Mac OSX I get a "Network is unreachable" error momentarily once when the network cable is re-plugged and DHCP hasn't yet acquired a new IP address.

所以基本上:我如何在MacOSX上实现本地客户端可以 仍从本地发件人接收?通过检测网络丢失 就像我在Linux上一样,或者通过欺骗使其表现得像Windows.

So basically: How can I achieve that on MacOSX the local client can still receive from the local sender? Either by detecting a network loss like I do on Linux or by tricking it into behaving like Windows.

由对网络编程有更深入了解的人员提供的任何建议 比我拥有的要多得多.

Any advise by people who have a deeper insight into network programming than i have, is greatly appreciated.

推荐答案

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

When I encountered this problem, my solution was to arrange to get a notification from the OS when the network configuration has changed. When my program received that notification, it would wait a few seconds (to hopefully make sure the network configuration has finished changing), and then tear down and reconstruct all of its sockets. It's a pain, but it seems to work pretty well.

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

Of course, there is no OS-agnostic way (that I know of) to get a notification from the OS when the network config has changed, so I had to implement it differently under each OS.

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

For MacOS/X, I spawn a separate watch-the-network-config thread, which looks like this:

#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);
    }
 }

还有上面的函数调用的此设置/支持代码:

and there is also this setup/support code, called from the above function:

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解决方案感兴趣,我不想在此页面上添加太多垃圾邮件.

And there are equivalent (and also fairly obscure) mechanisms for getting a network-config-changed notification under Linux (using socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE))) and Windows (using NotifyAddrChange()) which I can post if they would be helpful, but I don't want to spam up this page too much if you are only interested in the MacOS/X solution.

这篇关于Windows-Linux-Mac上的UDP套接字网络断开连接行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆