2013-03-24 99 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,导致它被覆盖在回调和我的主循环中,以混淆。我想如果你不使用共享连接,你需要维护一个需要进一步处理的引用列表(并且处理每个引用),然后在完成时将它们销毁。共享连接方式似乎更容易。