この問題が発生したときの解決策は、ネットワーク構成が変更されたときにOSから通知を受け取るように手配することでした。私のプログラムがその通知を受け取ったとき、それは数秒待ってから(うまくいけばネットワーク構成の変更が完了したことを確認するために)、次にすべてのソケットを破棄して再構築します。苦痛ですが、かなりうまくいくようです。
もちろん、ネットワーク構成が変更されたときにOSから通知を受け取る(私が知っている)OSに依存しない方法はないので、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(socket(AF_NETLINK、SOCK_RAW、NETLINK_ROUTE)を使用)およびWindows(NotifyAddrChange()を使用)でnetwork-config-changed通知を取得するための同等の(そしてかなりあいまいな)メカニズムがあります。役に立ちましたが、MacOS / Xソリューションにのみ興味がある場合は、このページをあまりスパムしたくありません。