我有一个使用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,它是100%可靠的。我唯一可能的解释是,在WSAAsyncSelect注册套接字的处理消息之前,DNSServiceResolve函数试图调用回调函数,但我看不到任何解决方法。
我已经在这上面花了很长时间了,现在完全没有想法了。任何建议都是受欢迎的,即使他们是“这真的是一种愚蠢的方式,为什么你不做X,Y,Z”。
发布于 2013-08-27 04:39:53
我使用“共享连接”(参见dns_sd.h文档)调用DNSServiceBrowse,如下所示:
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方法中。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 (例如TXTRecordGetCount和TXTRecordGetItemAtIndex)获得。
对于共享的连接引用,当您使用完它们时,您必须根据父引用(或从父引用复制)释放每个引用。我认为当您将共享引用的副本传递给它们的某个函数时,DNS-SD API会执行一些引用计数(以及父/子关系)。同样,有关详细信息,请参阅文档。
一开始我试着不使用共享连接,我只是向下传递ServiceRef,导致它在回调中被覆盖,并且我的主循环变得混乱。我想,如果你不使用共享连接,你需要维护一个需要进一步处理的引用列表(并处理每个引用),然后在完成后销毁它们。共享连接方法似乎容易得多。
https://stackoverflow.com/questions/15604284
复制相似问题