Welcome!

Java Authors: Jeremy Geelan, Liz McMillan, Hari Gottipati, Tad Anderson, Yakov Fain

Related Topics: .NET

.NET: Article

Orcas' Hidden Gem - The Managed PNRP Stack

You keep hearing about Silverlight and the Entity Framework and Jasper and Astoria and who knows how many others

Kevin Hoffman's Blog

Lately there's been a lot of buzz about Orcas. You keep hearing about Silverlight and the Entity Framework and Jasper and Astoria and who knows how many other code names. One of the things that you don't hear about is PNRP. Why? I haven't the faintest idea because this is some of the coolest stuff to surface as a .NET API since .NET 1.0 reshaped our opinion of streams.

While experimenting with Orcas, I noticed that the PNRP (Peer Name Resolution Protocol) did in fact have a managed API. It is exposed through System.Net.PeerToPeer in the Beta 1 bits. PNRP is basically a service registry. Whenever you have a service that has become available that you want other clients on your network to know about, you register that information with PNRP. The great thing about PNRP is that you don't need a server like you do with DNS. You register the peer name with your local PNRP (Windows Service on XP SP 2 w/PNRP update or Vista RTM) service. If you are near other PNRP implementations, they will share information and propogate the service registration depending on the scope you've chosen for your service name.

Why is this cool and why should you care? Simple. Peer networking is not all about sharing music and downloading l33t w4rez. Let's say you have a corporate backbone that's running all of your company's systems. Now let's say that you bring up a client application (which could be a desktop app or even a public-facing web application). How does your client application know where to go in order to find the service for submitting purchase orders? Sure, you could hard-code it, or even put it in an App.config, but a lot of the time people actually end up writing complex infrastructures to deal with service registries. Also, let's face it, UDDI sucks.

What if your client application were to be able to come up and do a quick PNRP query and get results that indicate there are 3 servers on the network currently implementing the purchase order service. Additionally, each service registry entry has a little bit of info to help your client application pick which service to use. This is where it gets good: WCF + PNRP = Pure Gold.

So I set off to write a sample application. Basically I created a couple of host servers that pretend to be photo libraries. As each service comes up, it not only turns on the WCF listeners, but it registers the service location with PNRP. Then, a single client comes up and queries the list of available network photo albums. For each one that it finds, it dynamically connects to that photo album and retrieves an icon representing the album by invoking a method on the advertised WCF photo album service.

The end result is an application that looks like this:

It might not look like much, but this application is enumerating a list of WCF services that were dynamically registered by WCF host applications. In addition, the client application is connecting to each of these advertised services and querying the service for additional information (in this case, an icon). 

To take a look at how ridiculously easy this is, here's the code that starts the WCF photo album service and registers a publicly available peer name with PNRP:

ServiceHost host = new ServiceHost(typeof(PhotoService));
host.Open();
PeerName pName = new PeerName(
                 "PhotoService",
                 PeerNameType.Unsecured);
PeerNameRegistration reg = new PeerNameRegistration(
                           pName, Int32.Parse(advertPort));
reg.Comment = advertName;
reg.Data = System.Text.ASCIIEncoding.ASCII.GetBytes(
    advertData);
reg.Start();

The advertPort is a port number defined in the app.config file, and advertData is a string containing a comment description (such as "Bob's Photos") that was also defined in the app.config of the service host. There is a limited amount of data you can store on the peer name registration, so keep it short and sweet. If you need additional information about the service, you can connect to the service and invoke methods on it (like I"m doing to obtain the icon for the service).

Here's the code on the client that enumerates the list of all "PhotoService" services on the network registered with PNRP. For each enumerated service, the client ignores the IPv6 address, and connects to the IPv4 address, establishes the WCF proxy instance, obtains the image icon (in a Hex64 encoded string of bytes), and saves it (the idea was I might cache the file so I don't have to query every time), and then finally binds the GUI to the URI of the locally cached image:

public void EnumeratePhotoServices()
{
ModelRoot model = ModelRoot.Current;
model.DiscoveredServices.Clear();
PeerNameResolver resolver = new PeerNameResolver();
PeerNameRecordCollection peers =
    resolver.Resolve(new PeerName("0.PhotoService"));
foreach (PeerNameRecord peer in peers)
{
    Console.WriteLine("{0}", peer.PeerName.ToString());
    DiscoveredService service = new DiscoveredService();
    service.Name = peer.PeerName.ToString();
    service.Comment = peer.Comment;
    service.Data = System.Text.ASCIIEncoding.ASCII.GetString(
        peer.Data);
    service.EndPoints = peer.EndPointCollection;
    foreach (IPEndPoint ep in peer.EndPointCollection)
    {
        Console.WriteLine("Service discovered at {0}:{1}",
            ep.Address.ToString(),
            ep.Port.ToString());
        Console.WriteLine(ep.Address.AddressFamily.ToString());
        if (ep.Address.AddressFamily ==
System.Net.Sockets.AddressFamily.InterNetwork)
        {
            string Url = "net.tcp://" + ep.Address.ToString()
+ ":" + ep.Port.ToString() +
                "/PhotoService";                       
            NetTcpBinding binding = new NetTcpBinding();
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.MaxBufferSize = int.MaxValue;
            EndpointAddress epAddress = new EndpointAddress(Url);
            ChannelFactory<IPhotoService> fact = new
ChannelFactory<IPhotoService>(binding,
                                                     epAddress);
            IPhotoService photos = fact.CreateChannel();
            string iconByteString = photos.GetIconBytes();
            byte[] iconBytes =
Convert.FromBase64String(iconByteString);
            Console.WriteLine("received {0}
image bytes.", iconBytes.Length);                       
            FileStream fs = File.Create(@"C:\" +ep.Address.ToString()
+"_" + ep.Port.ToString() + ".png");
            fs.Write(iconBytes, 0, iconBytes.Length);
            fs.Close();
           
            service.Icon = @"C:\" + ep.Address.ToString()
+ "_" + ep.Port.ToString() + ".png";
           
        }
    }





    model.DiscoveredServices.Add(service);
  }
}

In short, if you're using WCF and you're using Orcas, you need to give the PNRP stack a look, because being able to dynamically, in a shared, serverless, peer fashion, register the location of services is extremely useful.

tags:      
links: digg this    del.icio.us  technorati  reddit

More Stories By Kevin Hoffman

Kevin Hoffman, editor-in-chief of SYS-CON's iPhone Developer's Journal, has been programming since he was 10 and has written everything from DOS shareware to n-tier, enterprise web applications in VB, C++, Delphi, and C. Hoffman is coauthor of Professional .NET Framework (Wrox Press) and co-author with Robert Foster of Microsoft SharePoint 2007 Development Unleashed. He authors The .NET Addict's Blog at .NET Developer's Journal.

Comments (3) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
.NET News 05/12/07 10:18:25 AM EDT

Lately there's been a lot of buzz about Orcas. You keep hearing about Silverlight and the Entity Framework and Jasper and Astoria and who knows how many other code names. One of the things that you don't hear about is PNRP. Why? I haven't the faintest idea because this is some of the coolest stuff to surface as a .NET API since .NET 1.0 reshaped our opinion of streams.

.NET News 05/12/07 10:18:15 AM EDT

Lately there's been a lot of buzz about Orcas. You keep hearing about Silverlight and the Entity Framework and Jasper and Astoria and who knows how many other code names. One of the things that you don't hear about is PNRP. Why? I haven't the faintest idea because this is some of the coolest stuff to surface as a .NET API since .NET 1.0 reshaped our opinion of streams.

.NET News 05/12/07 10:17:58 AM EDT

Lately there's been a lot of buzz about Orcas. You keep hearing about Silverlight and the Entity Framework and Jasper and Astoria and who knows how many other code names. One of the things that you don't hear about is PNRP. Why? I haven't the faintest idea because this is some of the coolest stuff to surface as a .NET API since .NET 1.0 reshaped our opinion of streams.