MSDN Magazin > Home > Ausgaben > 2007 > September >  Netzwerk: Verbindungsherstellung mit .NET ...
Netzwerk
Verbindungsherstellung mit .NET Framework 3.5
Mariya Atanasova and Larry Cleeton and Mike Flasko and Amit Paka

Themen in diesem Artikel:
  • Leistung der Socket-Klasse
  • Internationale URLs
  • Der System.Net.PeerToPeer-Namespace
In diesem Artikel werden folgende Technologien verwendet:
.NET Framework
In Microsoft® .NET Framework macht der System.Net-Namespace die Funktionalität zahlreicher Netzwerkprotokolle wie z. B. HTTP und SMTP verfügbar. Die bevorstehende Veröffentlichung von .NET Framework 3.5 (in Visual Studio® 2008 enthalten und früher mit dem Codenamen „Orcas“ bezeichnet) umfasst eine Reihe von Leistungs- und Funktionsverbesserungen dieser wichtigen Netzwerkschichten. In diesem Artikel werden drei wichtige Veränderungen betrachtet, die vom System.Net-Team bereitgestellt wurden:
  • Hochleistungs-Socket-API
  • International Resource Identifier-Unterstützung für URIs
  • Peer-to-Peer-Namespace (P2P)
Mit der neuen Version von .NET Framework 3.5 werden Änderungen am Framework selbst eingeführt. Die in diesem Artikel beschriebenen Features sind in der Beta 1-Version von Visual Studio 2008 verfügbar, die von der MSDN®-Website heruntergeladen werden kann.

Leistung der Socket-Klasse
In Version 2.0 von .NET Framework stellt der System.Net.Sockets-Namespace eine Socket-Klasse bereit, die praktisch die gesamte Funktionalität der Windows® WinSock Win32®-APIs bietet. Diese Funktionalität ist in einer Klasse mit Methoden und Eigenschaften für Entwickler von verwaltetem Code enthalten. Für Socket gibt es einen Satz von synchronen Methoden, einschließlich Send und Receive, mit verschiedenen Parameterüberladungen für eine Vielzahl von Szenarios. Diese synchronen Methoden sind leicht zu verwenden und sehr gut für einfache Netzwerkaufgaben geeignet, bei denen Sockets verwendet werden. Es gibt auch einen Satz asynchroner Methoden für Socket, die auf dem asynchronen Programmiermodell (APM) basieren, das in .NET Framework breite Verwendung findet (weitere Informationen finden Sie unter msdn.microsoft.com/msdnmag/issues/07/03/ConcurrentAffairs). Diese asynchronen Methoden ermöglichen die relativ einfache asynchrone Verwendung der Socket-Klasse und bieten eine Möglichkeit, zahlreiche Sockets oder viele Sende- und Empfangsvorgänge auf einer Reihe von Sockets zu verarbeiten.
Die Socket-Klasse in Version 2.0 ist für verschiedene Clientanwendungen geeignet, für die Netzwerksockets verwendet werden müssen, sowie für einige Server- und Dienstanwendungen. Leider können einige Dienstanwendungsszenarios nicht mit der Socket-Klasse in Version 2.0 realisiert werden, aber sie sind mit systemeigenen Sprachen möglich, die die Windows WinSock-APIs direkt verwenden. Das Hauptproblem bei der Socket-Klasse in Version 2.0 besteht darin, dass sie zu viele CPU-Zyklen zum Durchführen eines einzelnen Socket-E/A-Vorgangs und beim Zuordnen der notwendigen zugrunde liegenden Objekte verbraucht, um E/A-Vorgänge auf einer großen Anzahl Sockets gleichzeitig zu verwalten.
Mit .NET Framework 3.5 kann nun eine große Anzahl von Overlapped-Objekten von der Common Language Runtime (CLR) effizient gleichzeitig verwaltet werden. Overlapped-Objekte in der CLR umschließen wirksam die systemeigene Windows-OVERLAPPED-Struktur, die zum Verwalten asynchroner E/A-Vorgänge verwendet wird. Es gibt eine Overlapped-Objektinstanz für jeden asynchronen Socket-E/A-Vorgang, der durchgeführt wird. Es ist jetzt möglich, 60.000 oder mehr verbundene Sockets zu haben, während ein ausstehender asynchroner Empfangs-E/A-Vorgang auf jedem Socket verwaltet wird.
Die Socket-Klasse in Version 2.0 verwendet Windows-E/A-Abschlussports für den Abschluss asynchroner E/A-Vorgänge. So können Anwendungen leicht für eine große Anzahl geöffneter Sockets skaliert werden. .NET Framework implementiert die System.Threading.ThreadPool-Klasse, die die Abschlussthreads zum Lesen von Abschlussports und zum Abschließen asynchroner E/A-Vorgänge bereitstellt. Bei der Entwicklung der zukünftigen Version 3.5 von .NET Framework steht das Beseitigen von Mehraufwand in den Codepfaden zwischen dem Lesen eines Abschlussports und dem Aufrufen des Abschlussdelegaten einer Anwendung oder dem Signalisieren des E/A-Abschlussereignisobjekts in einem IAsyncResult-Objekt stark im Mittelpunkt.
Das APM in .NET Framework wird auch als Begin/End-Muster bezeichnet. Das liegt daran, dass eine Begin-Methode zum Initiieren eines asynchronen Vorgangs aufgerufen wird, die ein IAsyncResult-Objekt zurückgibt. Ein Delegat kann optional als Parameter für die Begin-Methode bereitgestellt werden, die bei Abschluss des asynchronen Vorgangs aufgerufen wird. Alternativ kann ein Thread auf IAsyncResult.AsyncWaitHandle warten. Bei Aufruf des Rückrufs oder Signalisieren der Wartezeit wird die End-Methode aufgerufen, um die Ergebnisse des asynchronen Vorgangs abzurufen. Dieses Muster ist flexibel, verhältnismäßig einfach zu verwenden und in .NET Framework häufig anzutreffen.
Sie sollten jedoch beachten, dass es beim Durchführen einer beträchtlichen Anzahl asynchroner Socketvorgänge ein Problem gibt. Für jeden Vorgang muss ein IAsyncResult-Objekt erstellt werden, das nicht wiederverwendet werden kann. Dies kann sich durch intensive Objektzuordnung und Garbage Collection auf die Leistung auswirken. Zur Umgehung dieses Problems bietet die neue Version ein anderes Muster von Methoden für asynchrone E/A-Vorgänge mit Sockets. Bei diesem neuen Muster ist keine Zuordnung eines Vorgangskontextobjekts für jeden Socketvorgang erforderlich.
Statt ein völlig neues Muster zu erstellen, wird ein vorhandenes Muster verwendet, an dem eine grundlegende Änderung vorgenommen wurde. In der Socket-Klasse gibt es jetzt Methoden, für die eine Variation des ereignisbasierten Abschlussmodells verwendet wird. In Version 2.0 würde folgender Code verwendet, um einen asynchronen Sendevorgang auf einem Socket zu initiieren:
void OnSendCompletion(IAsyncResult ar) { }

IAsyncResult ar = socket.BeginSend(buffer, 0, buffer.Length, 
    SocketFlags.None, OnSendCompletion, state);
In der neuen Version ist auch Folgendes möglich:
void OnSendCompletion(object src, SocketAsyncEventArgs sae) { }

SocketAsyncEventArgs sae = new SocketAsyncEventArgs();
sae.Completed += OnSendCompletion;
sae.SetBuffer(buffer, 0, buffer.Length);
socket.SendAsync(sae);
Es gibt hier ein paar beachtenswerte Unterschiede. Das Objekt, das den Kontext des Vorgangs umschließt, ist ein SocketAsyncEventArgs-Objekt anstelle eines IAsyncResult-Objekts. Die Anwendung erstellt und verwaltet SocketAsyncEventArgs-Objekte und kann diese sogar wiederverwenden. Alle Parameter für den Socketvorgang werden durch Eigenschaften und Methoden des SocketAsyncEventArgs-Objekts angegeben. Der Abschlussstatus wird ebenfalls von Eigenschaften des SocketAsyncEventArgs-Objekts bereitgestellt. Schließlich ist die Verwendung einer Abschlussmethode für den Rückruf des Ereignishandlers erforderlich.
Alle diese Änderungen führen zu einer deutlich besseren Leistung und Skalierbarkeit der System.Net.Sockets-Klasse in .NET Framework 3.5. Zwei dieser Verbesserungen werden von vorhandenen Anwendungen automatisch übernommen. Die dritte Verbesserung, die neuen Socket-Methoden, kann nur beim Ändern einer Anwendung verwendet werden, aber diese Methoden bieten höhere Skalierbarkeit für die anspruchsvollsten socketbasierten Anwendungen.

International Resource Identifier-Unterstützung
Webadressen werden in der Regel mithilfe von Uniform Resource Identifiers (URIs) ausgedrückt, die aus einem sehr eingeschränkten Satz von Zeichen bestehen. Im Grunde enthalten sie nur Groß- und Kleinbuchstaben aus dem englischen Alphabet, Ziffern von 0 bis 9 und eine kleine Anzahl anderer ASCII-Symbole einschließlich Kommas und Bindestrichen.
Diese Syntax ist nicht sehr praktisch für jene Regionen der Welt, in denen ein anderer Zeichensatz als das lateinische Alphabet, beispielsweise Japanisch oder Hebräisch, verwendet wird. Für jemanden, der Englisch spricht, ist eine Adresse wie www.BaldwinMuseumOfScience.com leicht verständlich und einfach zu merken. Wenn dies jedoch nicht der Fall ist, sieht diese URL eher wie eine zufällige Anordnung von Symbolen aus. Wenn Sie nur Englisch sprechen, könnten Sie sich dann eine lange Adresse auf Chinesisch merken?
International Resource Identifiers (IRIs) unterstützen Nicht-ASCII-Zeichen oder genauer gesagt, Unicode/ISO 10646-Zeichen. Dies bedeutet, dass ein Domänenname Unicode-Zeichen enthalten kann, sodass eine URL beispielsweise folgendermaßen aussehen könnte: http://微軟香港.com.
Wir haben die vorhandene System.Uri-Klasse erweitert, sodass IRI-Unterstützung auf der Grundlage von RFC 3987 bereitgestellt wird (siehe faqs.org/rfcs/rfc3987.html). Derzeitige Benutzer werden keine Änderung gegenüber dem .NET Framework 2.0-Verhalten feststellen, es sei denn, sie entscheiden sich für die Aktivierung der IRI-Funktionen. Der Grund dafür ist, dass die Anwendungskompatibilität zwischen Version 3.5 und früheren Versionen sichergestellt werden soll.
Zur Aktivierung müssen zwei Änderungen durchgeführt werden. Zuerst fügen Sie der Datei „machine.config“ folgendes Element hinzu:
<section name="uri" type="System.Configuration.UriSection, System, 
                          Version=2.0.0.0, Culture=neutral, 
                          PublicKeyToken=b77a5c561934e089" />
Dann geben Sie an, ob die IDN-Analyse (Internationalized Domain Name, internationaler Domänenname) auf den Domänennamen angewendet werden soll und ob IRI-Analyseregeln anzuwenden sind. Dies kann in einer computerweiten machine.config oder in der Datei „app.config“ einer einzelnen Anwendung geschehen. Beispiel:
<configuration>
   <uri>
      <idn enabled="All" />
      <iriParsing enabled="true" />
   </uri>
</configuration>
Beim Aktivieren von IDN werden alle Unicode-Bezeichnungen in einem Domänennamen in ihre Punicode-Entsprechungen konvertiert. Punicode-Namen enthalten nur ASCII-Zeichen und beginnen immer mit dem Präfix „xn--“. Der Grund dafür ist, dass die meisten derzeit im Internet bereitgestellten DNS-Server nur ASCII-Zeichen unterstützen. Das Aktivieren von IDN wirkt sich nur auf den Wert der Uri.DnsSafeHost-Eigenschaft aus. Für 微軟香港.com enthält sie xn--g5tu63aivy37i.com, während Uri.Host die Unicode-Zeichen enthält.
Es gibt drei mögliche Werte für IDN, die Sie im enabled-Attribut des idn-Elements in Abhängigkeit von den von Ihnen verwendeten DNS-Servern verwenden können:
  • „All“ verwendet IDN-Namen (Punicode) für alle Domänen.
  • „AllExceptIntranet“ verwendet IDN-Namen für alle externen Domänen und Unicode-Namen für alle internen Domänen. Dieser Fall gilt nur, wenn die Intranet-DNS-Server Unicode-Namen unterstützen.
  • „None“ (der Standardwert) entspricht dem .NET Framework 2.0-Verhalten.
Das Aktivieren der IRI-Analyse (iriParsing enabled = "true") bewirkt eine Normalisierung und Zeichenüberprüfung gemäß den neuesten IRI-Regeln in RFC 3987. Der Standardwert ist „false“ und veranlasst eine Normalisierung und Zeichenüberprüfung gemäß RFC 2396 (siehe faqs.org/rfcs/rfc2396.html). Wenn Sie mehr über die URIs und die Uri-Klasse erfahren möchten, finden Sie Informationen in der Onlinedokumentation unter msdn2.microsoft.com/system.uri.

Der System.Net.PeerToPeer-Namespace
Ein interessanter neuer Namespace, der zu .NET Framework 3.5 hinzugefügt wurde, ist der System.Net.PeerToPeer-Namespace. Er befindet sich in der System.Net.dll-Assembly und stellt die wichtigsten Bausteine für das einfache Erstellen einer Peer-to-Peer-Anwendung (P2P) bereit. Der Namespace wurde gemäß den drei Phasen einer typischen P2P-Anwendung entwickelt: ermitteln, verbinden und kommunizieren. In der Ermittlungsphase geht es um die dynamische Suche nach Peers und ihren zugeordneten Netzwerkadressen. In der Verbindungsphase wird eine Netzwerkverbindung zwischen Peers hergestellt. In der Kommunikationsphase schließlich werden Daten zwischen ihnen ausgetauscht.
Die Features im System.Net.PeerToPeer-Namespace bieten eine Reihe von Optionen, die die Ermittlungs- und Verbindungsphase erleichtern. Abgesehen davon stellen ergänzende Technologien wie Sockets, HTTPWebRequest und Peer Channel von Windows Communication Foundation Lösungen für die Kommunikationsphase bereit.
Bevor eine Kommunikation zwischen Peers stattfinden kann, müssen sie in der Lage sein, einander zu ermitteln und ihre Netzwerkspeicherorte (Adressen, Protokolle und Ports) anhand von Namen oder anderen Arten von Bezeichnern aufzulösen. Wie diese Ermittlung erfolgt und Speicherorte aufgelöst werden, wird durch nicht permanente Konnektivität, fehlende Adresseinträge im DNS und dynamische IP-Adressen verkompliziert. Das in .NET Framework 3.5 unterstützte PNRP-Feature (Peer Name Resolution-Protokoll) ermöglicht die Ermittlung und die Kommunikation der Peers untereinander, indem die serverlose Namensauflösung beliebiger Ressourcen in einen Satz von Netzwerkendpunkten ermöglicht wird. PNRP ist verantwortlich für zwei Hauptaufgaben: das Veröffentlichen eines PeerName, der von anderen Peers aufgelöst werden muss, und das Auflösen eines PeerName, der von einem anderen Peer veröffentlicht wurde, und das Abrufen der zugehörigen Metadaten. (Weitere Informationen zur Funktionsweise des PNRP-Protokolls finden Sie unter microsoft.com/p2p.)
Im System.Net.PeerToPeer-Namespace stellt ein PeerName einen Endpunkt der Kommunikation dar, bei dem es sich um alles handeln kann (beispielsweise einen Computer, Dienst oder eine Anwendung), dem Sie Metadaten zuordnen möchten. PeerNames gibt es in zwei Formen: gesichert und ungesichert. Ein gesicherter PeerName wird von einem aus einem öffentlichen und einem privaten Schlüssel bestehenden Schlüsselpaar gesichert und kann bei der PNRP-Registrierung nicht manipuliert werden. Jede PeerName-Zeichenfolge weist einen Authority-Abschnitt auf, auf den ein Punkt und ein Classifier-Abschnitt folgen. Der Authority-Abschnitt wird auf der Grundlage des PeerName-Typs (gesichert oder ungesichert) computergeneriert, während es sich beim Klassifizierer um eine benutzerdefinierte Zeichenfolge handelt.
Für einen gesicherten PeerName erstellt das Framework automatisch eine Hexadezimalzeichenfolge von 40 Zeichen Länge als Authority. Diese Hexadezimalzeichenfolge ist ein Hash des öffentlichen Schlüssels, der dem PeerName zugeordnet ist, und dient zur Sicherstellung, dass Registrierungen solcher PeerNames nicht manipuliert werden können. So erstellen Sie einen sicheren PeerName:
PeerName p = new PeerName("My PeerName", PeerNameType.Secured);
Bei ungesicherten PeerNames ist die Authority-Komponente immer das Zeichen 0. Ein ungesicherter PeerName ist lediglich eine Zeichenfolge und bietet keine Sicherheitsgarantien:
PeerName p = new PeerName("My PeerName", PeerNameType.UnSecured);
Nach dem Erstellen eines PeerName muss der Name im nächsten Schritt relevanten Metadaten durch Instanziieren eines PeerNameRegistration-Objekts zugeordnet werden. Ein PeerName ist in der Regel einer IP-Adresse zugeordnet, doch ein PeerName kann auch einer Kommentarzeichenfolge und einem binären Daten-BLOB zugeordnet werden, wie Sie sehen werden. Im folgenden Codeausschnitt wird IPAddress explizit dem PeerName zugeordnet, indem der Endpunktauflistung der Registrierung eine PeerEndPoint-Instanz hinzugefügt wird:
PeerName peerName = new PeerName("My PeerName", PeerNameType.Secured);

PeerNameRegistration pnReg = new PeerNameRegistration();
pnReg.PeerName = peerName;
pnReg.EndPointCollection.Add(new IPEndPoint(
    IPAddress.Parse("<ip address to associate with the name>"), 5000)); 

pnReg.Comment = "up to 39 unicode char comment";
pnReg.Data = System.Text.Encoding.UTF8.GetBytes(
    "A data blob up to 4K associated with the name");
Obwohl dies eine gültige Verwendung der PeerNameRegistration-Klasse ist, besteht ein häufiges Szenario darin, dem PeerName alle Adressen zuzuweisen, die dem lokalen Computer zugeordnet sind. Dazu stellen Sie einfach sicher, dass die Eigenschaft PeerNameRegistration.UseAutoEndPointSelection auf „true“ gesetzt ist, und fügen PeerNameRegistration.EndPointCollection keine Elemente hinzu.
Jetzt, da dem PeerName alle relevanten Metadaten mithilfe des PeerNameRegistration-Objekts zugewiesen sind, besteht der letzte Schritt im Veröffentlichen des PeerName in einer Cloud, damit andere Peers den Namen auflösen können. In PNRP handelt es sich bei einer Cloud (Wolke) einfach um einen Satz von Computern, die an PNRP teilnehmen. Sie definiert, in welchem Bereich Namen veröffentlicht oder aufgelöst werden. Beim Veröffentlichen eines Namens müssen Sie festlegen, in welcher Cloud (oder welchem Bereich) der Name veröffentlicht werden soll.
PNRP verwendet derzeit zwei Clouds: verbindungslokale und globale. Bei einem in der verbindungslokalen Cloud veröffentlichten PeerName können nur andere Peers mit derselben Verbindung den Namen auflösen. Bei einem in der globalen Cloud veröffentlichten Namen kann jeder Computer im Internet den PeerName auflösen. Zum Veröffentlichen eines PeerName in der globalen Cloud weisen Sie einfach die globale Cloud über die Cloud.Global-Enumeration der Cloud-Eigenschaft im PeerNameRegistration-Objekt zu und rufen dann die Start-Methode für das Registrierungsobjekt auf. Nach Abschluss des Startaufrufs wird der Name veröffentlicht und kann von Remotepeers aufgelöst werden. Abbildung 1 zeigt den zum Erstellen und Veröffentlichen eines PeerName verwendeten Code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.PeerToPeer;

namespace CreateAndPublish
{
    class Program
    {
        static void Main(string[] args)
        {
            // Creates a secured PeerName.
            PeerName peerName = new PeerName(
                "MyWebServer", PeerNameType.Secured);

            PeerNameRegistration pnReg = new PeerNameRegistration();
            pnReg.PeerName = peerName;
            pnReg.Port = 80;

//Starting the registration means the name is published 
//for other peers to resolve.
            pnReg.Start();
            
            Console.WriteLine("Registration of Peer Name: {0} complete.", 
                peerName.ToString());

            Console.WriteLine("Press any key to stop the registration " +
                             "and close the program");
            Console.ReadKey();

            pnReg.Stop();
        }
    }
}

Sie wissen nun, wie ein PeerName erstellt und veröffentlicht wird. Jetzt geht es um seine Auflösung. Hierzu instanziieren Sie zunächst eine Instanz der PeerNameResolver-Klasse, die dann zum synchronen (siehe Abbildung 2) oder asynchronen Auflösen eines Namens verwendet wird. Wenn Sie Namen asynchron auflösen, sollten Sie beachten, dass zum Auflösen mehrerer PeerNames dieselbe PeerNameResolver-Klasse verwendet werden kann. Sie können also mehrere asynchrone Auflösungsvorgänge für verschiedene PeerNames starten, bevor der erste Vorgang abgeschlossen ist. So muss nicht für jeden parallel ablaufenden Auflösungsvorgang ein neues Resolver-Objekt instanziiert werden.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.PeerToPeer;
using System.Net;

namespace SyncResolve
{
    class Program
    {
        // The application accepts the peer name to resolve as the first
        // and only command line parameter.
        static void Main(string[] args)
        {
            // Create a resolver object to resolve a peername.
            PeerNameResolver resolver = new PeerNameResolver();
            PeerName peerName = new PeerName(args[0]);
            // Resolve the PeerName - this is a network operation and will
            // block until the resolve request is completed.
            PeerNameRecordCollection results = resolver.Resolve(peerName);

            // Show the data returned by the resolve operation.
            Console.WriteLine("Records from resolution of PeerName: {0}", 
                peerName);
            Console.WriteLine();
            int count = 1;
            foreach (PeerNameRecord record in results)
            {
                Console.WriteLine("Record #{0} results...", count);

                Console.WriteLine("Comment:");
                if (record.Comment != null)
                {
                    Console.WriteLine(record.Comment);
                }                

                Console.WriteLine("Data:");
                if (record.Data != null)
                {
                    //Assumes the data is an ASCII formatted string
                    Console.WriteLine(
                        System.Text.Encoding.ASCII.GetString(record.Data));
                }

                Console.WriteLine("Endpoints:");
                foreach (IPEndPoint endpoint in record.EndPointCollection)
                {
                    Console.WriteLine("\t Endpoint:{0}", endpoint);
                    Console.WriteLine();
                }

                count++;
            }

            Console.ReadKey();
        }
    }
}

Jetzt können Sie den in Abbildung 1 und Abbildung 2 dargestellten Code verwenden und versuchen, PeerNames zu erstellen und aufzulösen. Wenn Sie die PNRP-Funktionen ausprobieren, können Sie jedoch einen PeerName nicht mit demselben Prozess auflösen, mit dem der Name veröffentlicht wurde. Es sollte auch erwähnt werden, dass die hier erörterten PNRP-APIs die Windows-PNRP-Infrastruktur verwenden und in Windows XP (wenn das unter support.microsoft.com/kb/920342 verfügbare PNRP-Update installiert ist), Windows Vista® und Windows Server 2008 unterstützt werden.
Mit .NET Framework 3.5 wird auch der System.Net.PeerToPeer.Collaboration-Namespace eingeführt. Die Zusammenarbeit funktioniert in zwei Kontexten. Im ersten Kontext – „Personen in meiner Umgebung“ – geht es um Benutzer, die bei derselben Infrastruktur für Zusammenarbeit angemeldet sind und sich im selben Subnetz befinden. (Weitere Einzelheiten finden Sie unter microsoft.com/technet/network/p2p/pnm.mspx.) Bei dem zweiten als „Kontakte“ bezeichneten Kontext geht es um Personen, die Ihrem Windows-Adressbuch hinzugefügt wurden (es befindet sich in Ihrem Benutzerverzeichnis im Ordner „Kontakte“). Im Kontext „Kontakte“ müssen sich Benutzer nicht im selben Subnetz befinden.
Beide Kontexte ermöglichen es Benutzern, Anwendungen und Objekte freizugeben, Einladungen zu senden und über Ereignisse benachrichtigt zu werden, an denen andere Benutzer beteiligt sind. Ein Beispiel: Eine Mitarbeiterin der Vertriebsabteilung muss sich mit ihren Kollegen „treffen“, während sie am Flughafen auf einen Flug wartet. Im Idealfall sollte sie in der Lage sein, ihren Laptop einzuschalten, die zentrale Branchenanwendung ihres Unternehmens zu starten und mithilfe der Anwendung ihre Kollegen dynamisch zu finden, unabhängig davon, ob diese sich ebenfalls am Flughafen aufhalten oder am Schreibtisch im Büro sitzen. Die Anwendung muss sich zuerst bei der Collaboration-Infrastruktur anmelden und den Bereich der Anmeldung angeben. In diesem Beispiel umfasst PeerScope.All sowohl den Internetbereich als auch Peers in der Umgebung (PeerNearMe).
PeerCollaboration.Signin(PeerScope.All);
Nach der Anmeldung sucht die Anwendung mithilfe des folgenden Codes alle Personen in der Nähe dieser Vertriebsmitarbeiterin:
PeerNearMeCollection peers = PeerCollaboration.GetPeersNearMe();
PeerNearMeCollection enthält eine Instanz der PeerNearMe-Klasse für jeden Peer, der sich im selben Subnetz wie der Aufrufer befindet. Folglich verfügt die Anwendung nun über eine Liste aller „Personen in meiner Umgebung“ in Form von PeerNearMe-Instanzen. Eine PeerNearMe-Instanz umfasst eine Eigenschaft, die den Netzwerkspeicherort oder IPEndPoint (IP-Adresse + Port) des Remotepeers angibt.
Um nach allen im Adressbuch der Vertriebsmitarbeiterin gespeicherten Kontakten zu suchen, muss die Anwendung alle Kontakte mit den folgenden Codezeilen abrufen:
ContactManager contactManager = PeerCollaboration.ContactManager();
PeerContactCollection contacts = contactManager.GetContacts();
ContactManager stellt das Windows-Adressbuch dar, das der von der System.Net.PeerToPeer.Collaboration-Infrastruktur verwendete Kontaktspeicher ist.
Nach dem Generieren einer Liste aller (aus PeerNearMe und PeerContact bestehenden) Peers kann die Vertriebsmitarbeiterin entscheiden, mit welchen Kollegen sie interagieren möchte, und den Betreffenden schnell Einladungen senden. Eine Einladung im Kontext von System.Net.PeerToPeer.Collaboration ist eine Methode, mit der ein Remotepeer aufgefordert wird, eine bestimmte Anwendung zu starten. Um eine Netzwerkverbindung zwischen zwei Peercomputern herzustellen, muss einer der Peers eingehende Daten aktiv abhören. Diese Methode ermöglicht es einem Peer, einem anderen Peer mitzuteilen, welche Anwendung ausgeführt werden muss.
Angenommen, die Vertriebsmitarbeiterin in unserem Beispiel möchte mit einem Kollegen mithilfe der Branchenanwendung des Unternehmens interagieren. Sie muss sicherstellen, dass ihr Kollege dieselbe Branchenanwendung ausführt. Dazu sendet sie eine Einladung an den Kollegen. Wenn das System des Kollegen die Einladung empfängt, wird dem Benutzer ein Dialogfeld angezeigt, das ihm mitteilt, dass eine bestimmte Person möchte, dass er eine bestimmte Anwendung startet (siehe Abbildung 3). Das Dialogfeld bietet die Option zum Annehmen oder Ablehnen der Einladung. Wenn der Empfänger auf die Schaltfläche „Annehmen“ klickt, wird die in der Einladung erwähnte Anwendung (in diesem Fall die Branchenanwendung) automatisch auf dem Computer des Empfängers gestartet, sofern sie bereits installiert ist.
Abbildung 3 Einladung von einem Peer zum Starten einer Anwendung (Klicken Sie zum Vergrößern auf das Bild)
Die erforderliche Anwendung wird nun bei beiden Kollegen auf dem jeweiligen System ausgeführt, und sie können mit der Zusammenarbeit beginnen. Jetzt verwendet die Branchenanwendung eine andere Netzwerktechnologie (z. B. Peer Channel von Windows Communication Foundation, Sockets oder HTTP) für die Kommunikation.
Nach der Beschreibung von Einladungen geht es nun um die Einzelheiten und um den erforderlichen Code zum Aktivieren von Einladungen mithilfe des System.Net.PeerToPeer.Collaboration-Namespace. In Bezug auf die P2P-Infrastruktur handelt es sich bei einer Anwendung um eine ausführbare Datei auf Ihrem Computer. Um Einladungen zu unterstützen, muss die Anwendung mithilfe derselben GUID auf dem Computer des Einladenden und des Eingeladenen bei der Infrastruktur für die Peerzusammenarbeit registriert werden. Dieser Code zeigt, wie eine Anwendung auf der Grundlage einer ausführbaren Datei erstellt und registriert wird, die auf dem lokalen Computer installiert ist:
PeerApplication application = new PeerApplication(
    appGuid, "Collaboration Application", bytes, pathToApp, 
    arguments, PeerScope.Internet);
PeerCollaboration.RegisterApplication(
    application, PeerApplicationRegistrationType.AllUsers);
Registrierte Anwendungen, die über Einladungen gestartet werden, können mithilfe von PeerCollaboration.ApplicationLaunchInfo abfragen, welcher Kontakt oder Endpunkt die Einladung gesendet hat. Dadurch weiß die gerade gestartete Anwendung, dass sie über eine Einladung von einem Remotepeer gestartet wurde und dass sie eine Verbindung zu dem Peer herstellen muss, der die Einladung gesendet hat. Eine Einladung kann gesendet werden, indem folgendermaßen Invite für das Peerobjekt, das von GetPeersNearMe zurückgegeben wurde, oder ein Kontakt, der von GetContacts mit Invite zurückgegeben wurde, aufgerufen wird:
PeerInvitationResponse pir = peerNearMe.Invite(
    app, "Hello World", data);

PeerInvitationResponse pir = contact.Invite(
    app, "Hello World", data);
Angenommen, die Vertriebsmitarbeiterin kommuniziert mit einem Kollegen am Flughafen und plant, später nachzukommen, wenn sich die beiden an verschiedenen Orten aufhalten. In diesem Fall sollte sie diese Person als Kontakt auf ihrem Computer hinzufügen. Dies geschieht, indem die vom GetPeersNearMe-Aufruf zurückgegebene Peerklasse mit peer.AddToContactManager verwendet wird. Nachdem die Vertriebsmitarbeiterin und ihr Kollege einander als Kontakte hinzugefügt haben, können sie einander an jedem beliebigen Ort suchen und miteinander interagieren.
Die APIs für Zusammenarbeit in .NET Framework 3.5 ermöglichen auch das Freigeben von Daten. Wenn beispielsweise alle Vertriebsmitarbeiter des Unternehmens ihre Visitenkarten elektronisch freigeben möchten, kann dies mithilfe von Peerobjekten geschehen. Bei Peerobjekten handelt es sich einfach um Daten-BLOBs (beispielsweise eine Bilddatei), die von Remotepeers angezeigt werden können. Objekte können nach Bereich freigegeben werden. So kann beispielsweise „Personen in meiner Umgebung“ angegeben werden, damit Benutzer im selben Subnetz die Objekte anzeigen können. Sie können Objekte auch im Internetbereich freigeben, sodass Ihre Kontakte die Objekte unabhängig davon anzeigen können, wo sich die Benutzer aufhalten. Zum Erstellen eines Objekts und zu seiner Freigabe im Bereich „Personen in meiner Umgebung“ gehen Sie beispielsweise einfach wie folgt vor:
PeerObject object = new PeerObject(
    objectGuid, bytes, PeerScope.NearMe);
PeerCollaboration.SetObject(object);
Anwesenheits- und Änderungsbenachrichtigungen sind ebenfalls ein wichtiges Element der P2P-Zusammenarbeit. Angenommen, die Vertriebsmitarbeiterin möchte mit ihrem Kollegen Steve am Flughafen chatten, sobald dieser sich angemeldet hat und zur Verfügung steht. Ein Ereignis mit der Bezeichnung „PeerNearMeChanged“ wird für die PeerNearMe-Klasse bereitgestellt, um Peers im selben Subnetz zu aktualisieren. Änderungen an dem Peer sorgen dafür, dass der angefügte Delegat mit den Änderungsinformationen aufgerufen wird. Auf diese Weise kann die Vertriebsmitarbeiterin benachrichtigt werden, wenn Steve das Netzwerk aufruft (oder verlässt). Der erforderliche Code sieht folgendermaßen aus:
PeerNearMe.PeerNearMeChanged += PeerNearMeChangedCallback;
...
void PeerNearMeChangedCallback(
    object sender, PeerNearMeChangedEventArgs args)
{
        // Check which PeerNearMe has changed and what the change was
        // from the args parameter.
}
Bei Änderungsbenachrichtigungen für Kontakte an einem anderen Ort sind einige zusätzliche Schritte erforderlich. Dieser Prozess erfordert gegenseitiges Vertrauen, d. h. Sie müssen entscheiden, den Kontakt zu überwachen, und der Betreffende muss dies zulassen. Zuerst entscheiden Sie sich also dafür, den Kontakt zu überwachen, indem Sie die Subscribe-Methode für den Kontakt aufrufen. Dann muss der Peer, den Sie überwachen möchten, Sie als Kontakt hinzufügen und die Überwachung zulassen, indem er die SubscribeAllowed-Eigenschaft für Ihren Kontakt auf SubscriptionType.Allowed festlegt. Nach dem Durchführen dieser Schritte können Sie bestimmte Änderungen an Namen, Objekten, Anwendungen und so weiter für den von Ihnen überwachten Peer verfolgen.
Angenommen, die Vertriebsmitarbeiterin möchte mit einem Kundenkontakt chatten, sobald dieser sich online meldet. Mithilfe des folgenden Codes empfängt die Vertriebsmitarbeiterin die erforderlichen Informationen zu Statusänderungen des Kunden:
custContact.PresenceChanged += ContactPresenceChangedCallback;
...
void ContactPresenceChangedCallback(
    object sender, PresenceChangedEventArgs args)
{
    if (args.PeerPresenceInfo.PresenceStatus ==  
        PeerPresenceStatus.Online)
    {
        // Start chatting with the customer
    }
}
Beachten Sie, dass Änderungsinformationen auch für ein bestimmtes Objekt oder eine Anwendung gelten können. Durch Anfügen eines Delegaten an das ObjectChanged-Ereignis in der PeerObject-Klasse werden Informationen zu Änderungen an diesem Objekt sowie dazu bereitgestellt, woher das geänderte Objekt stammt, beispielsweise von einem Kontakt oder einem PeerNearMe. Auf ähnliche Weise werden durch Hinzufügen eines Delegaten zum ApplicationChanged-Ereignis in PeerApplication Informationen über Änderungen an dieser Anwendung bereitgestellt.
Um beispielsweise zu überwachen, ob sich eine bestimmte Anwendung auf der Liste der Anwendungen, die von einem beliebigen Kontakt in Ihrer Kontaktliste verfügbar gemacht wurde, geändert hat, muss dem ApplicationChanged-Ereignis für PeerApplication ein Delegat hinzugefügt werden:
peerApplication.ApplicationChanged += AppChangedCallback;
...
void AppChangedCallback(
    object sender, ApplicationChangedEventArgs args) 
{
    // Check what the change was and which contact and endpoint it
    // originated from the args parameter.
}
Betrachten wir nun eine Situation, in der die Vertriebsmitarbeiterin beschäftigt ist, sodass ihr Status geändert werden muss, um Peers zu informieren, dass sie derzeit nicht sofort verfügbar ist. Dies erfolgt durch Ändern der Daten, die der PeerCollaboration.LocalPresence-Eigenschaft zugeordnet sind, beispielsweise wie folgt:
PeerCollaboration.LocalPresenceInfo = new PeerPresenceInfo(
    PeerPresenceStatus.Away, "Talking with Customer");

Zusammenfassung
Dieser Artikel bietet nur einen kurzen Überblick über die neuen wichtigen Netzwerkfunktionen, die .NET Framework 3.5 hinzugefügt werden. Wenn Sie es nicht abwarten können, steht die aktuelle CTP-Version unter msdn2.microsoft.com/aa700831 zum Herunterladen bereit, sodass Sie diese neuen Features ausprobieren können. Um die aktuellen Nachrichten in Bezug auf Systems.Net und Windows-Netzwerktechnologien zu erhalten, besuchen Sie den Windows Network Developer Platform-Teamblog unter blogs.msdn.com/wndp.

Mariya Atanasova ist als Software Design Engineer in Test im System.Net-Team tätig. Sie kann über ihren Blog unter blogs.msdn.com/mariya erreicht werden.

Larry Cleeton ist als Software Design Engineer im System.Net-Team tätig. Sie erreichen ihn über seinen Blog unter blogs.msdn.com/lcleeton.

Mike Flasko ist der Programmmanager für die System.Net-, Winsock- und Winsock Kernel-Teams. Er kann über seinen Blog unter blogs.msdn.com/mflasko erreicht werden.

Amit Paka ist als Software Design Engineer im System.Net-Team tätig. Sie erreichen ihn über seinen Blog unter blogs.msdn.com/amitpaka.

Page view tracker