我使用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和a上的机器上部署了它 Mac OSX Leopard。我很高兴所有人都能进行多播 平台使用从示例派生的代码开箱即用。
我遇到问题的时候,是我断开网线的时候。的 当然,断开电缆总是会引起问题;)但是那里 微妙的差异让我发疯。
我的测试设置总是如下:一台机器运行发送器和 接收器,以查看同一台机器是否接收到自己的多播,以及 另一台只运行接收器的机器。我拉网线 运行发送方和接收方的机器。
观察到的行为:
- 显然,接收器运行的机器不再接收 消息。这是预期的;)
- 当网络电缆拔出的机器运行窗口时, 发送方继续发送,同一台机器上的接收方继续 受到。没有检测到错误。看来windows有一个内在的 回退到环回?
- 当拔掉网线的机器运行Mac OSX时, 发件人继续发送没有显示错误信息,但是 同一台机器上的接收器不再接收。在你问之前,我 选中NOT不设置禁用环回选项。
- 当拔掉网线的机器运行Linux时, 发送程序失败并出现提升::错误“网络无法访问”。明显, 由于发送方无法发送数据,因此接收方无法接收 任何事情。
对于Linux,我可以通过捕获来伪造Windows的行为 “无法访问”错误(或写入错误的字节数)和 在我的代码中设置一个标志,随后将所有数据发送到127.0.0.1 而不是多播地址。我经常检查一下send_to 多播端点仍会产生错误以检测网络重新连接 然后回到多播。这就像一个魅力,因为 接收器是bind()到inaddr_any,因此也在127.0.0.1上监听。
对于Mac OSX,我无法注意网络何时成为 无法访问以保持本地计算机上接收器的服务。
我发现在Mac OSX上我收到“网络无法访问”错误 重新插入网络电缆并且DHCP没有重新插入时 但是获得了一个新的IP地址。
基本上如此:我如何在MacOSX上实现本地客户端可以实现的目标 仍然从本地发件人收到?通过检测网络丢失 就像我在Linux上做的那样,或者通过欺骗它来表现得像Windows一样。
任何对网络编程有更深入了解的人的建议 我非常感激。非常感谢。
答案 0 :(得分:1)
当我遇到此问题时,我的解决方案是安排在网络配置发生变化时从操作系统获取通知。当我的程序收到该通知时,它将等待几秒钟(希望确保网络配置已完成更改),然后拆除并重新构建其所有套接字。这很痛苦,但似乎效果很好。
当然,当网络配置发生变化时,没有与OS无关的方式(我知道)从操作系统获得通知,所以我必须在每个操作系统下以不同的方式实现它。
对于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下(使用套接字(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE)))和Windows(使用NotifyAddrChange())获取网络配置更改通知的等效(也相当模糊)机制,我可以发布它们会有所帮助,但如果您只对MacOS / X解决方案感兴趣,我不想过多地向这个页面发送垃圾邮件。
答案 1 :(得分: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&amp;电缆连接或断开时的路由。