Channels ▼
RSS

Tools

DNS Service Discovery on Windows


Threading Issues

My program manages the Bonjour callbacks in a fairly ugly fashion. When my browsing activity starts, I create a timer that fires once every 250 milliseconds. I process up to 10 callbacks in that timer call, then exit. This continues until there are no pending browser or resolution requests; at which time, I kill the timer.

Depending on your use of DNS-SD, you may find that this is not as efficient as you'd like. If this is the case, you might find it useful to move your message pump code to a separate thread.

Once you do that, you can wait on all your callbacks by calling select with a long or infinite timeout. This will block your callback thread until it has actual work to do — resulting in a better use of CPU time.

There are some obvious downsides to this approach. Clearly, you have to use some sort of locking mechanism on the data structures that are shared between your callback thread and the rest of your program. And the use of the select() statement with an infinite timeout is complicated by the possibility that you may be making or canceling browsing or resolution calls while your program runs.

A good way to deal with both of these problems is to invoke a socket-based message-passing protocol between the callback thread and the other components of your program. If you restrict your interface to messages, you don't have to worry about locking access to shared data. And because you are using a socket for communications, your select() statement will be used to activate the thread when new messages arrive.

Character Sets

The days when DNS was limited to seven-bit ASCII characters are long gone. Service instances are encoded as UTF-8, and can use whatever Unicode characters they like. In Figure 3, you can see the effects of that when I browse for instances of iTunes:

Figure 3: Character set problems in service instance names.

Note that OS X users have so-called curly quotes in their library instance names, and curly quotes are definitely outside the range of seven-bit ASCII. DNS-SD collects the names as UTF-8 encoded strings, and sends them to the console in that format.

By default, the Windows cmd.exe window doesn't render UTF-8 properly, but changing the code page to 65001 results in the correct rendering.

In my sample program, I deal with this with a two step approach. First, my program is built using the Unicode libraries, ensuring that I am able to render Unicode output properly. To conform with Microsoft's C++ paradigms, I use CString for all my Unicode strings, and wrap all my string literals in the _T() wrapper.

This works fine for my UI, but I can't use strings built of wchar_t to communicate with the Bonjour SDK — it expects eight bit characters with UTF-8 encoding. In my program, I always use the C++ std::string class when I am working with 8-bit characters that might be encoded in UTF-8. When it comes time to render one of those strings in my Unicode context, all I have to do is use the handy CA2T macro with the CP_UTF8 parameter, and things work properly.

Library Issues

The design of the Bonjour SDK imposes some uncomfortable restrictions on you when it comes to building your C or C++ program. Because you are linking directly to code found in the dnssd.lib library, you have to ensure that your program and that library link against the same version of the C runtime library. And for the Bonjour SDK under Windows, this means you must link with the static, multithreaded, release version of the library.

You'll see the problem in this right away when you create an MFC project and try to build with dnssd.lib. By default, the project generator will probably have you using MFC in a shared DLL, and using the Multithreaded Debug DLL version of the C libraries. When you try to build like this, you will get some unpleasant error messages:

1>LINK : warning LNK4098: 
         defaultlib 'msvcrtd.lib' conflicts with use of other libs; 
         use /NODEFAULTLIB:library
1>LINK : warning LNK4098: 
         defaultlib 'LIBCMT' conflicts with use of other libs; 
         use /NODEFAULTLIB:library

A full-featured SDK would provide libraries built for multiple scenarios, and you would pick the one of your choice depending on your build parameters. But with the Bonjour SDK, you don't get this choice, so you need to ensure that your project follows a few guidelines:

  • Under Configuration Properties/General, field Use of MFC needs to be set to Use MFC in a Static Library for both debug and release builds.
  • Under Configuration Properties/C++/Code Generation, field Runtime Library needs to be set to Multi-threaded (/MT) for both debug and release builds.
  • Under Configuration Properties/C++/Preprocessor, field Preprocessor Definitions, the constant _DEBUG needs to be changed to NDEBUG for Debug configurations.

To build a project that uses the SDK, you will also need to add dns_sd.lib to your list of linker inputs, add dns_sd.h to your header files, and add the appropriate directories in the configuration under Configuration Properties/C++/General/ in field Additional Include Directories, and under Configuration Properties/Linker/General/ in field Additional Include Directories.

Overview Of the Demo Program

I've included the full source for a project that will build with Visual Studio 2010, as long as you have the Bonjour SDK installed. It browses all available services on the network and displays the information about them in tree form.

The program starts by kicking off a browser for _services._dns-sd._udp. The results are processed in member function IterateServiceTypes(). As each new service type is discovered, it is added to the tree, and a call to DNSServiceBrowse()is made to discover all instances of that service type. The callback for that browse call is member function IterateServiceInstances().

In IterateServiceInstances(), I add the instance to the tree, then call DNSServiceResolve(). This function operates much like the browse function, but it actually gets the DNS record for the service. This record contains the host name, service port, and a list of name/value pairs that a service can advertise as part of its record. You can see those values put to good work with service types like _ipp._tcp, in which printer parameters are exposed as part of service discovery.

ResolveInstance() is the callback routine that receives the information about the service instance. The host name, port, and name/value pairs are added to the tree; then one final call is made to a Bonjour SDK entry called DNSServiceGetAddrInfo(). This function resolves the IP address for the given host name. The address is stuffed into the tree in callback function GetAddress().

Conclusion

DNS Service Discovery is powerful tool, but Windows programmers might be put off by the lack of a nicely packaged SDK. Using this simple example program might be a good way to get comfortable with a powerful tool and a good multi-platform alternative to UPnP.

Sample program source

>Sample program executable


Mark Nelson is an engineer with Cisco Systems and a contributing editor to Dr. Dobb's.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video