2013-03-24 37 views
2

我有一個使用MFC構建的應用程序,我需要添加Bonjour/Zeroconf服務發現。我在確定如何最好地做到這一點時遇到了一些問題,但是我已經決定使用mDNSresponder源代碼中提供的DLL存根,並將我的應用程序鏈接到由其生成的靜態lib(它依次使用系統dnssd.dll)。使用MFC的Windows上的DNS-SD

但是,我仍然有問題,因爲回調似乎並不總是被稱爲所以我的設備發現停滯。令我困惑的是,在OSX下,使用OSX dns-sd終端服務以及在Windows下使用dns-sd命令行服務,它一切正常。在此基礎上,我排除了客戶端服務問題,並試圖找出我的Windows代碼出了什麼問題。

我基本上調用DNSBrowseService(),然後在該回調調用DNSServiceResolve(),然後最後調用DNSServiceGetAddrInfo()獲取設備的IP地址,以便我可以連接到它。

所有這些要求都是基於使用WSAAsyncSelect這樣的:

DNSServiceErrorType err = DNSServiceResolve(&client,kDNSServiceFlagsWakeOnResolve, 
                interfaceIndex, 
                serviceName, 
                regtype, 
                replyDomain, 
                ResolveInstance, 
                context); 

    if(err == 0) 
    { 
     err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(client), p->m_hWnd, MESSAGE_HANDLE_MDNS_EVENT, FD_READ|FD_CLOSE); 
    } 

但有時回調只是不會被調用即使該服務是存在的,使用命令行會確認。

我完全難住爲什麼這不是100%可靠,但它是如果我從命令行使用相同的DLL。我唯一可能的解釋是DNSServiceResolve函數在WSAAsyncSelect註冊套接字的處理消息之前嘗試調用回調函數,但是我看不到任何解決方法。

我已經花了很多年了,現在完全沒有想法。任何建議都會受到歡迎,即使它們「這是一種非常愚蠢的做法,爲什麼你不做X,Y,Z」。

回答

0

我打電話DNSServiceBrowse,具有「共享連接」(見dns_sd.h對於文件)如:

DNSServiceCreateConnection(&ServiceRef); 
// Need to copy the main ref to another variable. 
DNSServiceRef BrowseServiceRef = ServiceRef; 
DNSServiceBrowse(&BrowseServiceRef,    // Receives reference to Bonjour browser object. 
       kDNSServiceFlagsShareConnection, // Indicate it's a shared connection. 
       kDNSServiceInterfaceIndexAny, // Browse on all network interfaces. 
       "_servicename._tcp",    // Browse for service types. 
       NULL,       // Browse on the default domain (e.g. local.). 
       BrowserCallBack,     // Callback function when Bonjour events occur. 
       this);       // Callback context. 

這是稱爲ServiceDiscovery一個線程類的主run方法內。 ServiceRefServiceDiscovery的成員。

於是馬上按照上面的代碼中,我有一個主事件循環如下所示:

while (true) 
{ 
    err = DNSServiceProcessResult(ServiceRef); 
    if (err != kDNSServiceErr_NoError) 
    { 
     DNSServiceRefDeallocate(BrowseServiceRef); 
     DNSServiceRefDeallocate(ServiceRef); 
     ServiceRef = nullptr; 
    } 
} 

然後,在BrowserCallback你必須設置決心要求:

void DNSSD_API ServiceDiscovery::BrowserCallBack(DNSServiceRef inServiceRef, 
               DNSServiceFlags inFlags, 
               uint32_t inIFI, 
               DNSServiceErrorType inError, 
               const char* inName, 
               const char* inType, 
               const char* inDomain, 
               void* inContext) 
{ 
    (void) inServiceRef; // Unused 

    ServiceDiscovery* sd = (ServiceDiscovery*)inContext; 
    ... 
    // Pass a copy of the main DNSServiceRef (just a pointer). We don't 
    // hang to the local copy since it's passed in the resolve callback, 
    // where we deallocate it. 
    DNSServiceRef resolveServiceRef = sd->ServiceRef; 
    DNSServiceErrorType err = 
     DNSServiceResolve(&resolveServiceRef, 
         kDNSServiceFlagsShareConnection, // Indicate it's a shared connection. 
         inIFI, 
         inName, 
         inType, 
         inDomain, 
         ResolveCallBack, 
         sd); 

然後在ResolveCallback你應該擁有你需要的一切。

// Callback for Bonjour resolve events. 
void DNSSD_API ServiceDiscovery::ResolveCallBack(DNSServiceRef inServiceRef, 
               DNSServiceFlags inFlags, 
               uint32_t inIFI, 
               DNSServiceErrorType inError, 
               const char* fullname, 
               const char* hosttarget, 
               uint16_t port,  /* In network byte order */ 
               uint16_t txtLen, 
               const unsigned char* txtRecord, 
               void* inContext) 
{ 
    ServiceDiscovery* sd = (ServiceDiscovery*)inContext; 
    assert(sd); 

    // Save off the connection info, get TXT records, etc. 
    ... 

    // Deallocate the DNSServiceRef. 
    DNSServiceRefDeallocate(inServiceRef); 
} 

hosttargetport包含您的連接信息,並且可以使用DNS-SD API(例如TXTRecordGetCountTXTRecordGetItemAtIndex)獲得的任何文字記錄。

使用共享連接引用時,必須在完成每個引用時基於(或從中複製父引用)來釋放每個引用。當您將共享引用的副本傳遞給其中一個函數時,我認爲DNS-SD API會執行一些引用計數(以及父/子關係)。再次請參閱文檔以獲取詳細信息。

我嘗試不使用共享連接,我只是傳遞ServiceRef,導致它被覆蓋在回調和我的主循環中,以混淆。我想如果你不使用共享連接,你需要維護一個需要進一步處理的引用列表(並且處理每個引用),然後在完成時將它們銷燬。共享連接方式似乎更容易。