2010-08-31 37 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