Windows XP en Vista bieden ondersteuning voor peer-to-peer netwerken. Peer-to-peer netwerken zijn vooral bekend door de file-sharing netwerken, zoals KaZaA en BitTorrent en de applicaties die van deze netwerken gebruik maken. Maar peer-to-peer netwerken en -applicaties bieden meer mogelijkheden dan alleen het delen van bestanden. Zo kun je andere resources delen, bijvoorbeeld de rekenkracht van je machine, of online collaboratie vereenvoudigen. Toch is peer-to-peer nog niet goed bekend bij de meeste .NET ontwikkelaars. Daar komt verandering in: het .NET Framework 3.5 heeft een managed API gekregen voor het bouwen van peer-to-peer en collaboratieve applicaties met behulp van de voorzieningen in het besturingsysteem. In dit eerste artikel over peer-to-peer applicaties kijken we naar peer-to-peer terminology, het PNRP protocol en de bijbehorende managed API plus een simpele toepassing met behulp van de WCF PeerChannel API.
Peer-to-peer netwerken
Peer-to-peer (P2P) netwerken zijn er in verschillende soorten en maten. Een P2P netwerk is een normaal IP-gebaseerd netwerk. In het netwerk komen peers voor, die ook wel nodes worden genoemd. De nodes kunnen allerlei soorten resources zijn, zoals een applicatie, een gebruiker, een computer of anderssoortige hardware (printer, mobiele telefoon of zelfs een regensensor met IP adres).
De peers in een P2P netwerk zijn met elkaar verbonden. De verzameling van peers die onderling verbonden zijn wordt een mesh of graph genoemd. Het is mogelijk dat een P2P netwerk meerdere meshes bevat, aangezien de groepen peers niet met elkaar verbonden hoeven te zijn. In een volledig verbonden mesh hebben peers verbindingen met alle andere peers. Naarmate het aantal peers in een netwerk groter wordt is het steeds moeilijker om rechtstreekse verbindingen te behouden. Het is vaak voldoende om alle peers via-via met elkaar te verbinden. Dit zijn de zogenaamde “partially connected” meshes.

Fig. 1: Verschillende soorten meshes
De communicatie in een partially connected mesh kun je vergelijken met de community van .NET ontwikkelaars in Nederland
De communicatie in een partially connected mesh kun je vergelijken met de community van .NET ontwikkelaars in Nederland. Om met alle ontwikkelaars te kunnen communiceren hoef je niet rechtstreeks met iedereen te praten. Het is voldoende om je boodschap aan een kleine groep kennissen te vertellen, die het doorvertellen aan hun kennissen, enz., totdat iedereen de boodschap heeft gehad. Op eenzelfde manier kun je ook met een enkeling communiceren, al is het direct doorgeven van boodschappen dan gerichter. Het aantal spillen, de topologie en de indeling van het netwerk is afhankelijk van de opzet van het P2P netwerk.
Applicaties voor peer-to-peer netwerken
De gemiddelde netwerk applicatie bestaat uit clients en servers. Denk aan een database applicatie en database server, of browser en webserver. De client en server hebben daarbij duidelijk verschillende rollen. De client werkt samen met de server, die altijd aanwezig is, een vast en bekend netwerk adres heeft en daarmee makkelijk te bereiken is. De clients communiceren onderling niet rechtstreeks, maar gebruiken de server als tussenpersoon, zoals bij een chat-applicatie en –server. Daardoor hoeven de clients elkaar niet te kennen of te kunnen bereiken.
Een applicatie die gebruik maakt van een peer-to-peer netwerk gedraagt zich doorgaans anders. De applicatie meldt zich vanaf de host machine aan op dit netwerk en wordt daarmee een peer in een mesh. De machine heeft doorgaans een wisselend netwerk adres. Verder moeten de clients rechtstreeks met elkaar kunnen communiceren, liefst zonder tussenkomst van centrale servers.
De doorsnee handelingen van een P2P applicatie zijn:
- Aanmelden op een P2P netwerk
De applicatie meldt zich aan op een P2P netwerk en voegt zich bij een bestaande mesh van peers. De nieuwe peer zal één of meerdere verbindingen met peer nodes aangaan. Hierna maakt de applicatie echt deel uit van het mesh. Als er nog geen mesh is, zorgt de applicatie voor het aanmaken van het mesh.
- Communicatie met alle peers
De applicatie kan via het mesh eenvoudig met alle peers tegelijk communiceren. De applicatie voert dan een broadcast uit over het mesh. De broadcast start met het aanspreken van de directe peers, die de boodschap weer doorspelen naar hun directe peers, enz.
- Interactie met specifieke peers
De applicatie kan ook samenwerken met bewust gekozen peers. Het is soms nl. wenselijk dat er directe communicatie plaatsvindt en niet via-via. De directe interactie is alleen mogelijk wanneer het netwerkadres van de gewenste peers achterhaald kan worden. Daarna kan de applicatie via gangbare mechanismen communicatie starten tussen de peers, bijvoorbeeld met behulp van sockets, ASMX web services of WCF services.
De peers van een pure P2P applicatie zijn allemaal gelijkwaardig. De minder pure vormen van P2P applicaties kennen een soort van centralisatie, doordat er dedicated servers in het netwerk zijn die coördinatie en administratie van het netwerk verzorgen. Soms zijn er in het P2P netwerk peers met een speciale functie (ook wel super-peers genoemd) aanwezig. Dit betekent veelal dat er een bestaande infrastructuur nodig is om de P2P applicatie te kunnen laten functioneren. De pure P2P applicaties kunnen juist ad-hoc functioneren.
Peer name resolution
Microsoft heeft bij de implementatie van peer-to-peer netwerken gekozen voor het IPv6 protocol. Als je nog niet bekend bent met IPv6 kun je op http://technet.microsoft.com meer informatie vinden door te zoeken op “IPv6 technical reference”.
Beschikbare IPv4 adressen zijn schaars; met IPv6 kan alles en iedereen een adres krijgen
De reden om te kiezen voor IPv6 is dat de beschikbare IPv4 adressen (2^32 mogelijkheden) inmiddels schaars zijn, terwijl IPv6 (2^128 mogelijkheden) nog meer dan genoeg adressen beschikbaar heeft voor alle computers, devices en applicaties die er nog gemaakt gaan worden in de nabije toekomst. Dit betekent voor het P2P netwerk dat iedere peer zijn eigen unieke IPv6 adres zou kunnen krijgen.
De immense hoeveelheid IPv6 adressen en ook mogelijke peers in een P2P netwerk brengt diverse uitdagingen met zich mee voor het P2P netwerk en de applicaties. Ten eerste draait het grootste deel van de huidige netwerk-infrastructuur nog op IPv4, dat standaard gescheiden is van een IPv6 netwerk. Dit probleem heeft Microsoft opgelost met een UDP tunnelling protocol genaamd Teredo. Dit protocol zorgt voor de overgang van een IPv4 netwerk naar het IPv6 netwerk en eventueel nog terug. Zo kunnen resources met een IPv6 adres binnen een IPv4 netwerk communiceren met en via IPv6 netwerken.
Verder zal het in een IPv6 gebaseerd netwerk moeilijk zijn om andere peers te vinden zonder gebruik te maken van centrale servers. Microsoft heeft hiervoor het Peer Name Resolution Protocol (PNRP) ontworpen en geïmplementeerd. PNRP is een gedistribueerd en (nagenoeg) serverloos protocol, gemaakt voor schaalbare en betrouwbare naam-publicatie en -resolutie.
Het protocol maakt gebruik van PNRP identifiers. Een PNRP ID is een 256 bit unsigned integer, bestaande uit een 128 bit hash van een PNRP peer name (ook wel P2P ID) en een 128 bit gegenereerd nummer voor de service location, dat een indirecte verwijzing is naar de locatie van de peer. Het service location deel in het PNRP ID maakt deel uit van een numerieke namespace waarbij de getallen relatieve “afstanden” voorstellen tussen adressen van de peers. Hiermee kan tijdens een zoektocht naar een specifieke peer steeds dichter naar die peer genavigeerd worden.

Fig. 2: PNRP
(Bron: Microsoft TechNet (http://technet.microsoft.com/en-us/library/bb726971(en-us).aspx))
Een voorbeeld van een PNRP ID is:
198e25a029dfaef373bd86adac7dd2ef.69b5db7be977e1870aa923376f5f5d6e
Een PNRP publisher registreert zijn zelfgekozen peer naam en bijbehorende IPv6 eindpunten bij een mesh waar het deel van uit wil gaan maken. Een PNRP resolver kan op basis van de peer naam of het PNRP ID op zoek gaan naar een specifieke peer en daarvan de geregistreerde eindpunten achterhalen.
Het elegante van het PNRP protocol is de gedistribueerde cache van PNRP IDs die wordt bijgehouden door alle geregistreerde peers. Iedere peer houdt een meerlaagse cache bij van geregistreerde IDs. Tijdens het resolven van een peer naam wordt er via de caches van peers gezocht naar de uiteindelijke locatie van de registrerende peer. Laten we dit nog eens vergelijken met het zoeken naar een specifieke .NET ontwikkelaar. Stel je bent op zoek naar een zekere programmeur in Leeuwarden, terwijl je zelf in een ander deel van Nederland zit. Je kent alleen de naam van die programmeur en hebt geen adres. Je kunt dan je directe kennissen vragen of zij die persoon kennen. Zij kennen (in hun eigen adressenlijst c.q. cache) wel een aantal personen, maar waarschijnlijk niet die ene persoon. Als zij iemand kennen die meer in de buurt woont, bijvoorbeeld in Drenthe of Friesland, dan geven zij het adres van die persoon door. Vervolgens kun je aan die persoon vragen of deze de gezochte persoon kent. Zo kom je alsmaar dichterbij en vind je uiteindelijk hopelijk de juiste persoon. Een programmeur hoeft in dit scenario niet alle anderen te kennen. Het is voldoende om binnen je eigen straat, stad en je provincie een relatief klein aantal personen te kennen. Dit komt overeen met de meerlaagse cache van PNRP. Het maakt het PNRP protocol schaalbaar voor grote aantallen peers, zoals in de orde van tientallen of honderden miljoenen.
Verder krijgt iedere PNRP peer een unieke DNS naam eindigend op pnrp.net, waarmee je via het netwerk overal te vinden en bereiken bent. Dit geldt zowel voor applicaties als andere resources die gebruik maken van PNRP. Zo kan je computer zich ook registreren en een DNS naam krijgen. Een dergelijke naam heet een Windows Internet Computer Name (WICN).
PNRP onderscheidt een aantal clouds waarbinnen geregistreerde peers zich bevinden. Een cloud wordt gevormd door de peer nodes die zich daarin bevinden. PNRP heeft clouds op basis van het type IPv6 adres. Deze soorten clouds zijn:
- Global: een globale cloud voor alle peers met een globaal IPv6 adres, dat geldig is op het gehele Internet.
- Link-local: locale (intra-net) cloud met peers die een zelf-toegewezen IPv6 adres hebben.
De term link-local slaat op lokale netwerk-link (link = interface card), oftewel een lokaal netwerk via een specifieke netwerkkaart. Je kent de link-local adressen waarschijnlijk wel van IPv4, die beginnen met 169.254.*.*. Deze adressen worden door de netwerklaag toegewezen aan netwerkkaarten die geen DHCP server kunnen vinden om automatisch een IP adres te verkrijgen. Zo kunnen computers in een netwerk zonder DHCP toch met elkaar communiceren via het link-local netwerk. Eenzelfde principe wordt gehanteerd bij IPv6. De meeste infrastructuren hebben nog geen manier om lokale IPv6 adressen uit te delen. De netwerkkaarten krijgen daarom een link-local IPv6 adres beginnend met fe80:: toegewezen via het Neighborhood Discovery process. De grote beperking van link-local adressen is het lokale karakter: routers kunnen link-local verkeer niet doorsturen buiten de link.
- Site-local: Deze cloud wordt niet meer gebruikt.
De clouds zijn vooral relevant voor de scope van een geregistreerde peer naam. De peer namen zijn alleen geldig en vindbaar binnen een cloud. De link-local cloud is dus alleen interessant voor peer-to-peer applicaties die in een lokaal netwerk gebruikt gaan worden. Er zijn veel meer mogelijkheden in de global cloud, aangezien daarin peers uit het hele Internet voorkomen. Dit betekent dat peers niet op dezelfde lokale netwerk-link hoeven te zitten om met elkaar verbonden te zijn.
Een applicatie of resource die zich wil aanmelden op de PNRP global cloud moet allereerst een IPv6 adres hebben. Mocht de infrastructuur IPv6 voorzieningen hebben, dan is dit eenvoudig geregeld. Echter, meestal is het interne netwerk dat verbonden is aan het globale Internet een IPv4 netwerk en niet in staat om IPv6 adressen toe te wijzen. Het eerder genoemde Teredo protocol kan naast tunnelen van IPv4 naar IPv6 ook IPv6 adressen uitdelen aan Teredo clients. Deze adressen zijn te herkennen aan de 2001::0000::/32 prefix.
PNRP en het .NET Framework 3.5
Het .NET 3.5 Framework heeft een assembly System.Net.dll met daarin de namespaces System.Net.PeerToPeer en System.Net.PeerToPeer.Collaboration. We zullen in eerste instantie kijken naar de eerste namespace. De System.Net.PeerToPeer namespace bevat allerhande typen voor het werken met PNRP. Zo kun je bijvoorbeeld alle beschikbare PNRP clouds op een systeem als volgt enumereren:
CloudCollection clouds = Cloud.GetAvailableClouds();
foreach (Cloud cloud in clouds)
{
Console.WriteLine("Cloud {0} with scope {1} (ID={2})",
cloud.Name, cloud.Scope, cloud.ScopeId);
}
Ofschoon de eerder genoemde PNRP IDs een prominente rol binnen het protocol spelen, zal een applicatie zich alleen bemoeien met de peer naam. Een peer-to-peer applicatie die zichzelf vindbaar wil maken via PNRP zal zijn peer naam registreren. Een peer naam bestaat uit een autoriteit en classifier in de vorm {authority}.{classifier}. De classifier is een zelfgekozen en meestal makkelijk te lezen naam. De autoriteit wordt automatisch bepaald en geeft aan of de peer naam een secure of unsecure naam is. Unsecure peer namen geven geen garantie dat de naam authentiek is en zijn daarmee makkelijk te spoofen. De autoriteit is dan altijd 0. De secure namen daarentegen gebruiken een public/private key pair voor het berekenen van een hash voor de autoriteit. Het bezit van de private key bewijst dat de publisher daadwerkelijk eigenaar is van de peer naam.
Dit zijn een tweetal voorbeelden van peer namen:
- 0.LX (unsecure)
- 8249799f11314d1d3afea14b5e2ec32bf59d8574.LX (secure)
Registratie van peer namen
Het registreren van een peer naam gaat als volgt:
PeerName name =
new PeerName("LX", PeerNameType.Unsecured);
PeerNameRegistration registration =
new PeerNameRegistration(
name, 1337, Cloud.AllLinkLocal);
registration.UseAutoEndPointSelection = false;
IPAddress address =
IPAddress.Parse("IPv4 or IPv6 address");
IPEndPoint endPoint = new IPEndPoint(address, 1337);
registration.EndPointCollection.Add(endPoint);
registration.Comment = "GameStats version 1.0.23.110";
registration.Data =
Encoding.UTF8.GetBytes("LX game statistics: ...");
registration.Start();
Het PeerName object wordt geconstrueerd met de classifier “LX” en er is gekozen voor een niet secure peer naam. Vervolgens wordt de registratie uitgevoerd via de PeerNameRegistration klasse. Hierin wordt de peer naam meegegeven, een poort die gebruikt wordt voor de registratie en communicatie met de PNRP peers en de clouds waarin registratie moet plaatsvinden. Dit kan de global cloud of alle link-local clouds (zoals in het voorbeeld) zijn, alle beschikbare clouds of een specifieke selectie van beschikbare clouds.
De registratie van een peer name moet ertoe leiden dat de registrerende resource gevonden kan worden op een eindpunt. Deze eindpunten zijn IPv4 of IPv6 adressen met een bijbehorende poort. De registratie heeft daarom een collectie van IPEndPoint objecten nodig die gekoppeld gaan worden aan de peer naam. Tenslotte is het nog mogelijk om commentaar en data te bundelen met de geregistreerde naam. Het commentaar is bedoeld als een korte tekst van maximaal 39 karakters. De data mag een 4 KB grote byte array zijn en kan daarmee bijvoorbeeld geserializeerde objecten bevatten. Beide stukken informatie zouden moeten helpen om de juiste peer naam te selecteren.
De registratie wordt uitgevoerd bij de aanroep van de Start methode van het PeerRegistration object. De peer naam is daarna voor het betreffende AppDomain geregistreerd. Andere applicaties kunnen eventueel dezelfde peer naam registreren, maar blijven uniek vindbaar.
Resolven van peer namen
De volgende logische stap is het vinden van een specifieke peer met behulp van PNRP. Het zoeken naar een peer kan alleen vanuit een ander AppDomain dan diegene die heeft geregistreerd. De PeerNameResolver klasse probeert een peer name te vinden. De resolutie kan synchroon of asynchroon uitgevoerd worden. Het is aan te raden om de asynchrone variant te gebruiken, aangezien een peer name resolutie enige tijd in beslag kan nemen en de synchrone aanroep in die tijd blokkeert. Het onderstaande fragment toont hoe je asynchroon een peer name kunt proberen te achterhalen.
PeerNameResolver resolver = new PeerNameResolver();
resolver.ResolveCompleted += (sender, e) =>
{
foreach (PeerNameRecord record
in e.PeerNameRecordCollection)
{
Console.WriteLine("Peer {0} at address {1}",
record.PeerName, record.EndPointCollection[0]);
}
};
resolver.ResolveProgressChanged +=
(sender,e) => Console.WriteLine(e.ProgressPercentage);
Console.Write("Enter unsecure peer name: ");
string nameToResolve = Console.ReadLine();
PeerName name =
new PeerName(nameToResolve, PeerNameType.Unsecured);
resolver.ResolveAsync(name, nameToResolve);
Console.WriteLine("Waiting Press ENTER to quit");
Console.ReadLine();
De twee lambda expressions zijn de delegate methoden die worden aangeroepen bij de events ResolveProgressChanged en ResolveCompleted. Deze methoden hebben EventArgs die de voortgang van de resolutie dan wel de gevonden records bij de gezochte peer naam aangeven. De resolutie start bij de aanroep van ResolveAsync. De applicatie zal daarna wachten tot de operatie is voltooid. Het blokkeren van de main thread hoeft niet persé. Een WinForms of WPF applicatie kan doorgaan met het afhandelen van UI events. Het wachten tot de resolutie klaar is kan ook gedaan worden met een multithreading primitieve, zoals het AutoResetEvent.
Bouwen van P2P applicaties
Na al deze informatie vraag je jezelf wellicht af hoe hiermee een P2P applicatie gebouwd kan worden. P2P applicaties kunnen PNRP inzetten om meshes van peers te vormen binnen een PNRP cloud. Dit kan door slim gebruik te maken van geregistreerde peer namen. De applicatie kan met de peer namen de adressen van andere peers achterhalen en zelf de connecties met de andere peers regelen volgens een gekozen algoritme.
P2P applicaties met WCF PeerChannel
Een deel van de Windows Communication Foundation stack helpt je om P2P applicaties te maken. De PeerChannel klassen werken met meshes van peers in een P2P netwerk. WCF regelt en onderhoudt de connecties tussen de peers in het mesh en zorgt ervoor dat het mesh intact blijft. WCF gebruikt standaard PNRP om peer namen te registreren en later te kunnen resolven. Alle applicaties die het PeerChannel gebruiken, registreren dezelfde applicatie-specifieke unsecure naam middels PNRP. Hierna kan WCF alle peers in het mesh eenvoudig achterhalen door deze ene naam te resolven.
De beveiliging van het WCF mesh wordt niet met behulp van secure peer namen geregeld. Immers, iedere peer zou dan een naam met een verschillende autoriteit hebben, die niet bekend is bij de andere peers. In plaats daarvan beveiligt WCF de toegang tot het mesh met behulp van een wachtwoord of een certificaat. Dit gebeurt indirect, aangezien iedereen een peer naam kan registreren in het mesh en PNRP geen beveiliging kent. Een nieuwe peer in het mesh moet verbinding maken met een aantal nabij gelegen peers. Tijdens het opbouwen van de verbinding vindt authenticatie plaats middels het wachtwoord of het certificaat. De communicatie van de boodschappen tussen de peers is ook te beveiligen met encryptie op het transport of de boodschap.
PNRP stelt de nodige eisen aan de inrichting van het netwerk, zoals IPv6 en globale internet connectie. WCF kan daarom ook custom resolvers gebruiken voor het achterhalen van de peers in het mesh. De CustomPeerResolver Service is een custom resolver implementatie die wordt meegeleverd met WCF. Wanneer deze resolver service wordt gehost, kunnen PeerChannel gebaseerde applicaties de resolver gebruiken. De P2P applicatie is daardoor niet meer afhankelijk van PNRP, maar verliest zijn server-loze karakter.
Voorbeeld applicatie met WCF
Laten we eens kijken hoe met WCF een heel eenvoudige server-loze P2P chat applicatie gebouwd wordt. Allereerst definiëren we een interface met één enkele operatie voor het versturen van een chat boodschap naar alle peers in het P2P mesh. De operatie is one-way, aangezien er een broadcast gedaan wordt over het mesh en er geen antwoorden verwacht worden. Dit is typisch voor alle operaties van PeerChannel. Verder zullen de peers gebruik maken van een duplex channel, omdat ze zowel boodschappen ontvangen en ook op eigen initiatief boodschappen gaan versturen. Een duplex channel moet een service contract hebben dat een callback contract heeft. Het callback contract is hetzelfde gehouden als het gewone contract, aangezien de rol van iedere peer symmetrisch is verzenden en ontvangen.
[ServiceContract
(Namespace="urn:www-sdn-nl:services:p2p:chat",
CallbackContract=typeof(IChat))]
public interface IChat
{
[OperationContract(Name = "Say", IsOneWay = true)]
void Say(string text);
}
De implementatie van de service is simpelweg het schrijven van een binnenkomend bericht naar de console.
class ChatClient: IChat
{
#region IChat Members
public void Say(string text)
{
Console.WriteLine("{0}: {1}",
DateTime.Now.ToLongTimeString(), text);
}
#endregion
}
Nu zal het nodig zijn om een proxy aan te maken, zodat de services met elkaar kunnen communiceren. Wellicht dat het nu enigszins vreemd lijkt te worden. Immers, in een puur P2P netwerk komen alleen maar clients voor, maar nu hebben we het toch over een service met een proxy (oftewel client). In het geval van PeerChannel moet je dit wat ruimer zien. De implementatie van de service is die van de peer client. De proxy is de geijkte WCF manier om met een service te communiceren, ook al is dat in P2P een andere instantie van dezelfde service implementatie. Bedenk dat de service is feite een peer is, oftewel een client in het P2P netwerk. Alle peers zijn allemaal “servers” op het moment van een broadcast naar het mesh. Zij ontvangen allemaal de binnenkomende “client” boodschap.
In een puur P2P netwerk komen alleen maar clients voor
Jammer genoeg heeft het PeerChannel nog geen ondersteuning voor tooling als svcutil.exe en wsdl.exe. Het is dus niet mogelijk om met die tools een WCF proxy te maken. De benodigde code is ook met de hand te schrijven. De volgende interface zorgt voor een client channel waarmee de DuplexChannelFactory een proxy kan instantiëren.
public interface IChatChannel: IClientChannel, IChat {}
We maken verder geen volledige proxy klasse, maar laten de WCF infrastructuur een dynamische proxy genereren. Hieronder staat de code van de console applicatie die de WCF chat service gaat hosten.
class Program
{
static IChatChannel channel;
static void Main(string[] args)
{
ChatClient client = new ChatClient();
InstanceContext context =
new InstanceContext(client);
DuplexChannelFactory<IChatChannel> factory =
new DuplexChannelFactory<IChatChannel>
(context, "ChatClientEndPoint");
factory.Credentials.Peer.MeshPassword = "123";
channel = factory.CreateChannel();
PeerNode node = channel.GetProperty<PeerNode>();
channel.Open();
Console.WriteLine("Listening on port {0}", node.Port);
Console.WriteLine("Online? {0}", node.IsOnline);
while (true)
{
Console.Write("Say:> ");
string text = Console.ReadLine();
channel.Say(text);
}
}
}
Veel van de bovenstaande code is WCF specifiek. De dikgedrukte statements laten een aspecten van het P2P programmeren met WCF zien. Allereerst valt te zien hoe het wachtwoord voor de peer authenticatie in het mesh opgegeven wordt. Verder kan de service de peer node opvragen en daarmee achterhalen wat de poort is waarop de service gaat luisteren naar binnenkomend verkeer en of de node online is. Dat laatste verdient enige uitleg. Een peer in een mesh wordt gezien als online wanneer er meer dan één peer aanwezig is. Indien er maar één peer in het mesh is noemt men deze offline, ook al is er op dat moment wel degelijk een netwerkconnectie.
Tenslotte bevat de while loop de aanroep van de service operatie. Deze ene aanroep via de proxy vanuit de peer zal ertoe leiden dat alle peers (inclusief de peer zelf) in het mesh deze aanroep krijgen.
Een deel van de configuratie van de PeerChannel service staat in de app.config. Hierbij is het opvallend dat er alleen een <client> is geconfigureerd. Echter, in lijn met de eerdere opmerkingen was dit te verwachten.
<system.serviceModel>
<behaviors />
<client>
<endpoint name="ChatClientEndPoint"
binding="netPeerTcpBinding"
bindingConfiguration="PeerTransportBinding"
address="net.p2p://MeshName:1337/client"
contract="WcfPeerChannelDemo.IChat" />
</client>
<bindings>
<netPeerTcpBinding>
<binding name="PeerTransportBinding" port="0">
<security mode="Transport">
<transport credentialType="Password" />
</security>
<resolver mode="Pnrp" />
</binding>
</netPeerTcpBinding>
</bindings>
</system.serviceModel>
Het ABC van de client configuratie laat het volgende zien:
- Adres: Het scheme van het netwerkadres is net.p2p voor PeerChannel. Het host-adres definieert niet zozeer een bestaand IPv4 of IPv6 adres, maar de naam van het mesh waar de peer deel van gaat uitmaken.
- Binding: De binding voor PeerChannel is de netPeerTcpBinding. De bijbehorende binding-configuratie laat zien hoe in dit geval de beveiliging transport met wachtwoord is en dat de resolutie van peers met PNRP wordt gedaan. De poort die op 0 is ingesteld geeft aan dat iedere peer zelf een beschikbare poort mag kiezen om te luisteren naar inkomend verkeer. De implementatie van de console applicatie laat zien wat de poort daadwerkelijk is geworden.
- Contract: De IChat interface vormt het service contract.
Hieronder is de applicatie in werking te zien. Negeer het schoonheidsfoutje van de ontvangende clients die op de Say:> regel ook de ontvangen boodschappen tonen.

Fig. 3: Console applicatie running
Merk op hoe de eerst gestarte chat client (bovenste in de figuur) aangeeft offline te zijn bij het opstarten. De andere clients geven aan online te zijn.
Er zijn nog meer mogelijkheden met de PeerChannel API, zoals
- het luisteren naar statusveranderingen van een peer node;
- het gebruik van propagatiefilters die bepalen welke boodschappen doorgelaten worden;
- beperken van de broadcast door het limiteren van de hop-count (aantal nodes waarnaar gesprongen wordt);
- het schrijven van een eigen custom resolver.
Het PeerChannel in WCF werd al in versie 3.0 van het .NET Framework geïntroduceerd. De managed API voor PNRP zoals die in .NET 3.5 zit was toen nog niet beschikbaar. Vandaar dat WCF een eigen implementatie heeft. Nu de System.Net.PeerToPeer namespace beschikbaar is kan iedere ontwikkelaar die een P2P applicatie wil maken en daarbij een mesh wil inzetten gebruik maken van PNRP om dat te realiseren.
Samenvatting
De eerste stap bij het ontwikkelen van P2P applicaties is het kiezen van een geschikt P2P netwerk. Microsoft biedt met IPv6 en het PNRP protocol een goede infrastructuur om op te bouwen. Een applicatieontwikkelaar die P2P features in wil bouwen in zijn applicatie zal dus in contact komen met nieuwe protocollen als IPv6, Teredo en vooral PNRP. De managed APIs van het .NET Framework 3.5 bieden voorzieningen om met PNRP te werken en zo peers in een P2P netwerk te publiceren en te vinden en zodoende meshes te creëren. Tenslotte kun je met behulp van je favoriete communicatiemechanisme peers met elkaar laten samenwerken. Je grootste uitdaging voor het bouwen van een P2P applicatie is het bedenken van een goede toepassing.