【问题标题】:DNS-SD on Windows using MFC在 Windows 上使用 MFC 的 DNS-SD
【发布时间】:2013-03-24 21:34:24
【问题描述】:

我有一个使用 MFC 构建的应用程序,我需要将 Bonjour/Zeroconf 服务发现添加到其中。我在弄清楚如何最好地做到这一点时遇到了一些麻烦,但我已经决定使用 mDNSresponder 源代码中提供的 DLL 存根并将我的应用程序链接到由它生成的静态库(反过来使用系统dnssd.dll)。

但是,我仍然遇到问题,因为回调似乎并不总是被调用,因此我的设备发现停止了。让我感到困惑的是,在 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”。

【问题讨论】:

    标签: c++ mfc bonjour zeroconf dns-sd


    【解决方案1】:

    我打电话给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,导致它在回调中被覆盖,我的主循环变得混乱。我想如果您不使用共享连接,则需要维护一个需要进一步处理(并处理每个)的引用列表,然后在完成后销毁它们。共享连接方法似乎要容易得多。

    【讨论】:

      猜你喜欢
      • 2012-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多