2013-03-24 1 views
2

У меня есть приложение, построенное с использованием MFC, которое мне нужно добавить для обнаружения службы Bonjour/Zeroconf. Мне было сложно понять, как это сделать, но я решил использовать DLL-заглушку, предоставленную в исходном коде mDNSresponder, и привязать мое приложение к статическому lib, сгенерированному этим (что, в свою очередь, использует систему dnssd.dll).DNS-SD в Windows с использованием MFC

Однако у меня по-прежнему возникают проблемы, поскольку обратные вызовы не всегда вызываются так, что я обнаружил открытые устройства. Что меня смущает, так это то, что все работает отлично в 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. 

Это внутри основного run метода класса резьбы называется ServiceDiscovery. ServiceRef является членом 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); 
} 

hosttarget и port содержат вашу информацию о соединении, а также любые текстовые документы могут быть получены с помощью DNS-SD API (например, TXTRecordGetCount и TXTRecordGetItemAtIndex).

С помощью ссылок на общий доступ вы должны освободить каждого из них на основе (или скопированного) родительской ссылки, когда вы закончите с ними. Я думаю, что DNS-SD API выполняет некоторый подсчет ссылок (и отношения родителя/ребенка), когда вы передаете копии общей ссылки на одну из своих функций. Снова ознакомьтесь с документацией.

Сначала я попытался не использовать общие подключения, и я просто пропустил ServiceRef, в результате чего он был перезаписан в обратных вызовах и мой основной цикл, чтобы запутаться. Я предполагаю, что если вы не используете общие подключения, вам необходимо сохранить список ссылок, которые нуждаются в дальнейшей обработке (и обрабатывать каждый), а затем уничтожить их, когда вы закончите. Общий подход к подключению казался намного проще.

Смежные вопросы