ZeroConf Frequently Asked Questions


How can I enumerate all registered ZeroConf services?

Enumerating all registered ZeroConf services is a two-stage process.

Firstly, we should get all available service types.

This is done with special service request _services._dns-sd._udp. set to the Type property of the ZeroConfBrowser instance:

zeroConfBrowserStage1.Type = "_services._dns-sd._udp.";
zeroConfBrowserStage1.Active = true;

The ZeroConfBrowser will begin raising ServiceAdded events after activation.

Secondly, we should have a separate service browser for every discovered service type. Before we start browsing, we need to construct a valid service type query from the data returned by the first browser:

private void zeroConfBrowserStage1_ServiceAdded(object sender, RemObjects.SDK.ZeroConf.ZeroConfBrowseResultArgs args)
{
  string serviceType = String.Format("{0}.{1}.", args.Record.Name, args.Record.Type));
  // ...
}

The next step is to create a new service browser instance, set it up, activate it and get information about discovered servers of the current type in the ServiceAdded event handler of the newly created browser.

Check out the following C# code performing service discovery. The form has TreeView, Timer and ZeroConfBrowser components on it.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BonjourBrowseAll
{
    public partial class Form1 : Form
    {
        public Form1()
        {
          InitializeComponent();

          m_Browsers = new List<Browser>();
          zeroConfBrowserStage1.Type = "_services._dns-sd._udp.";
          zeroConfBrowserStage1.Active = true;

          viewTimer.Enabled = true;
        }

        // This class is responsible for discovering all servers of single service type.
        // It contains a browser and a service list.
        private class Browser
        {
            public Browser(string serviceType)
            {
              m_Services = new List<RemObjects.SDK.ZeroConf.ZeroConfService>();
              m_Browser = new RemObjects.SDK.ZeroConf.ZeroConfBrowser();
              m_Browser.Type = serviceType;
              m_Browser.ServiceAdded += m_Browser_ServiceAdded;
              m_Browser.ServiceRemoved += m_Browser_ServiceRemoved;
              m_Browser.Active = true;
            }

            private RemObjects.SDK.ZeroConf.ZeroConfBrowser m_Browser;
            private List<RemObjects.SDK.ZeroConf.ZeroConfService> m_Services;

            void  m_Browser_ServiceRemoved(object sender, RemObjects.SDK.ZeroConf.ZeroConfBrowseResultArgs args)
            {
          m_Services.Remove(args.Record);
            }

            void  m_Browser_ServiceAdded(object sender, RemObjects.SDK.ZeroConf.ZeroConfBrowseResultArgs args)
            {
          m_Services.Add(args.Record);
              args.Record.TryResolve();
            }
        
            // This is the optional method to get all services discovered so far or only resolved ones.
            // It uses LINQ to extract resolved services only.
            public List<RemObjects.SDK.ZeroConf.ZeroConfService> GetServices(bool resolvedOnly)
            {
              var res = new List<RemObjects.SDK.ZeroConf.ZeroConfService>(m_Services.Count);
              res.AddRange(m_Services.Where((item) => { return !resolvedOnly || item.Resolved; } ));
              return res;
            }

            public string ServiceType
            {
              get { return m_Browser.Type; }
            }
        }

        private List<Browser> m_Browsers;

        private void zeroConfBrowserStage1_ServiceAdded(object sender, RemObjects.SDK.ZeroConf.ZeroConfBrowseResultArgs args)
        {
          // The initial browser event handler. Constructing a service type string and creating
          // new service browser here.
          m_Browsers.Add(new Browser(String.Format("{0}.{1}.", args.Record.Name, args.Record.Type)));
        }

        // Because service discovery is done asynchronously we will periodically refresh the tree view displaying services.
        private void viewTimer_Tick(object sender, EventArgs e)
        {
          servicesView.BeginUpdate();
          try
          {
            servicesView.Nodes.Clear();
            foreach (Browser item in m_Browsers)
            {
              TreeNode node = servicesView.Nodes.Add(item.ServiceType);
              var services = item.GetServices(true);
              foreach (var serv in services)
                node.Nodes.Add(String.Format("{0}:{1}", serv.HostTarget, serv.Port));
            }
          }
          finally
          {
            servicesView.ExpandAll();
            servicesView.EndUpdate();
          }
        }
    }
}

How can I use ZeroConf for global discovery?

Theoretically it is necessary to provide yourcompany.yourdomain.com. (without quotes) as a domain. But unfortunately Bonjour for Windows doesn't supports wide area service advertisement and discovery, it is possible only for Bonjour for Mac OS X. No tests were made to find out if it will work if you build a server with FPC for OS X.

So you are limited to your LAN actually.