2

Bonjour/Zeroconf サービス検出を追加する必要がある MFC を使用して構築されたアプリケーションがあります。最善の方法を見つけるのに少し苦労しましたが、mDNSresponder ソース コードで提供されている DLL スタブを使用し、それによって生成された静的ライブラリにアプリケーションをリンクすることにしました (これは、システムを使用します)。 dnssd.dll)。

ただし、コールバックが常に呼び出されているとは限らないため、デバイスの検出が停止するため、まだ問題があります。私を混乱させているのは、OSX dns-sd ターミナル サービスを使用する OSX と dns-sd コマンド ライン サービスを使用する Windows では、すべて問題なく動作することです。その上で、クライアント サービスが問題であることを除外し、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 を使用した場合です。考えられる唯一の説明は、WSAAsyncSelect がソケットの処理メッセージを登録する前に、DNSServiceResolve 関数がコールバック関数を呼び出そうとすることですが、これを回避する方法がわかりません。

私はこれに何年も費やしてきましたが、今では完全にアイデアがありません。「それは本当にばかげた方法です。なぜX、Y、Zをやらないのですか」という提案であっても、どんな提案でも大歓迎です。

4

1 に答える 1

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.

runこれは、 というスレッド クラスのメイン メソッド内にありますServiceDiscoveryServiceRefのメンバーですServiceDiscovery

次に、上記のコードの直後に、次のようなメイン イベント ループがあります。

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 を使用して取得できます (例:TXTRecordGetCountおよびTXTRecordGetItemAtIndex)。

共有接続参照を使用すると、使用が終了したら、親参照に基づいて (または親参照からコピーして) それぞれの割り当てを解除する必要があります。DNS-SD API は、共有参照のコピーを関数の 1 つに渡すと、参照カウント (および親子関係) を行うと思います。繰り返しますが、詳細についてはドキュメントを参照してください。

最初は共有接続を使用しないようにしましたが、 を渡すだけだったServiceRefため、コールバックで上書きされ、メイン ループが混乱しました。共有接続を使用しない場合は、さらに処理が必要な参照のリストを維持し (そしてそれぞれを処理し)、完了したらそれらを破棄する必要があると思います。共有接続アプローチは、はるかに簡単に思えました。

于 2013-08-26T20:39:53.060 に答える