More with Discovery, Day 3


By now, you might be wondering where a person would actually use discovery. A common case would be allowing two processes on the same machine to find each other and allow for dynamic naming of all endpoints (such as using GUIDs in the URLs). This could be used by every Windows Service that has an application running in the system tray. In the enterprise, you would use discovery as one part of a publish and subscribe system. The subscribers would query for all endpoints that publish some kind of information (which would allow the subscribers to poll). Alternatively, a publisher could periodically ask for all entities on the network that were interested in a given topic and push to those endpoints. Likewise, a client could look for a service that implemented some other functionality and dynamically configure itself (instead of needing a priori knowledge about how infrastructure is deployed).

To make a service discoverable on the server, you add a ServiceDiscoveryBehavior to the service. This behavior, when combined with a UdpDiscoveryEndpoint, allows the service to be found over a broadcast message sent on the network. If you want clients to be able to automatically configure themselves, you need to add an IMetadataExchange endpoint as well. The IMetadataExchange endpoint allows the service to send information about the contracts in use, the bindings against those contracts, and address information on where the service is listening for messages.

The following code constructs a ServiceHost for some service, TestService, that implements a contract named ITest.

var baseUri = string.Format("net.tcp://{0}/Test", Environment.MachineName);
using (var host = new ServiceHost(typeof(TestService), new Uri(baseUri)))
{
  // Make the service discoverable and make sure it has an
  // endpoint to announce its presence.
  var discoveryBehavior = new ServiceDiscoveryBehavior();
  discoveryBehavior.AnnouncementEndpoints.Add(
    new UdpAnnouncementEndpoint());
  host.Description.Behaviors.Add(discoveryBehavior);

  // Make sure the service can respond to probes.
  host.AddServiceEndpoint(new UdpDiscoveryEndpoint());

  // Add the ability to handle Metadata requests (aka WSDL)
  host.Description.Behaviors.Add(new ServiceMetadataBehavior());
  host.AddServiceEndpoint(typeof(IMetadataExchange),
    MetadataExchangeBindings.CreateMexTcpBinding(), "/mex");

  // Tell the service to listen for ITest messages on net.tcp
  host.AddServiceEndpoint(typeof (ITest), new NetTcpBinding(),
    string.Empty);

  // Open the host and start listening.
  host.Open();

  // Display what we are listening for
  foreach (var endpoint in host.Description.Endpoints)
  {
    Console.WriteLine("{0}: {1}",
      endpoint.Contract.Name,
      endpoint.ListenUri);
  }

  // Wait until we are done.
  Console.WriteLine("Press [Enter] to exit");
  Console.ReadLine();
}

 

With this code, we have a service that is discoverable and callable without requiring a client to have any code or configuration that is specific to our service (though it may if the developer chooses).

%d bloggers like this: