Right, so in Part 1 we looked at an overview of how this discovery system should work. Now let's start looking at specifics. Before we start diving into the UDP stuff, let's look at the information that the server will publish and how this will be accomplished. If you recall, the mechanism I suggested was that the server host a normal WCF service that held the information about the services, and that the UDP would be used to provide access to this service. So let's start with the WCF side.
First off we're going to require our ServiceInfo class. This is a relatively simple class, which contains an Address property and a set of Name/Value pairs. Needless to say, it needs to be decorated with the DataContract attribute. To provide access to this information we will have the IDiscoveryService interface:
namespace Sanity.Net
{
[ServiceContract]
internal interface IDiscoveryService
{
[OperationContract]
ServiceInfoCollection GetServices();
}
}
You'll note that I made this interface internal, since there's no real call for outside applications to access it without going through the Discovery system we're writing. Our next step is to create a DiscoveryServer class which will take the address it needs to publish as a parameter in its constructor. In order to keep things simple, I'm only going to allow the default binding for the transport mechanism. As I discussed in Part 1, discovery is not the place to get fancy with communication methods, it's where you find out about the fancy communication methods.
We'll create a Publish method on the DsicoveryServer, that will create the service host and open the service endpoint:
private string _address;
private ServiceHost<IDiscoveryService> _host;
public void Publish()
{
// Set up the discovery service
_host = new ServiceHost<IDiscoveryService>(this, _address);
Binding binding = BindingAddressParser.CreateTransportBinding(_address);
_host.AddServiceEndpoint(typeof(IDiscoveryService), binding, _address);
_host.Open();
}
Now, you'll notice my BindingAddressParser, that's just a little class that will infer the binding from the address, NetTcpBinding for "net.tcp" and so forth.
Our next step is to add our ServiceInfoCollection to the class so that services can be given to the discovery server:
private ServiceInfoCollection _localServices;
public ServiceInfo AddService(string address)
{
address = BindingAddressParser.ResolveAddressHostName(address);
return _localServices.Add(address);
}
Now an interesting item is that ResolveAddressName. If you think about it publishing an address like
"net.tcp://localhost:21021/MyService" is not terribly useful, since we need to know where localhost actually
is. So ResolveAddressHostName uses DNS to replace localhost with the machines actual name. It will also resolve an IP addresses to the machines name as well (e.g. 127.0.0.1).
Finally, we can implement the IDiscoveryService interface:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class DiscoveryServer : DiscoveryBase, IDiscoveryService
{
ServiceInfoCollection IDiscoveryService.GetServices()
{
return _localServices;
}
}
Okay, we now have a server-side that will take services with arbitrary information and make them available on TCP/IP. In Part 3 we will look at making this available on UDP as well.