communityWir suchen ständig neue Tutorials und Artikel! Habt ihr selbst schonmal einen Artikel verfasst und seid bereit dieses Wissen mit der Community zu teilen? Oder würdet ihr gerne einmal über ein Thema schreiben das euch besonders auf dem Herzen liegt? Dann habt ihr nun die Gelegenheit eure Arbeit zu veröffentlichen und den Ruhm dafür zu ernten. Schreibt uns einfach eine Nachricht mit dem Betreff „Community Articles“ und helft mit das Angebot an guten Artikeln zu vergrößern. Als Autor werdet ihr für den internen Bereich freigeschaltet und könnt dort eurer literarischen Ader freien Lauf lassen.

TCP/IP Socket-Programmierung in C# Drucken E-Mail
Benutzerbewertung: / 1025
SchwachPerfekt 
Geschrieben von: Kristian   
Sonntag, den 12. März 2006 um 02:20 Uhr
Beitragsseiten
TCP/IP Socket-Programmierung in C#
Daten senden und empfangen
Höhere Socketklassen
Multicast und Broadcast
Ping, Threads und asynchrone Sockets
Formulare im Internet senden
E-Mail verschicken
Alle Seiten

Einführung

Als Ende der 1960er-Jahre die Vorgängerversion des heute bekannten Internets entstand, nämlich das ARPAnet, konnten sich die damals beteiligten Personen den Einfluss eines derartigen dezentralen Computernetzwerkes noch gar nicht vorstellen. Kein Wunder, diente es anfangs doch nur einigen amerikanischen Universitäten zu Forschungszwecken um ihre Computer zu vernetzen und so die Rechenzeit besser ausnutzen zu können. Das Internet entwickelte sich in der zweiten Hälfte des 20. Jahrhunderts kontinuierlich und neue Technologien brachten neue Formen dieses Computernetzwerkes hervor. Der Begriff des Internet etablierte sich Anfang der 1970er und das in der heutigen Form bekannte World Wide Web entstand erst Anfang der 1990er-Jahre am renommierten CERN, der Europäischen Organisation für Kernforschung.

Heute nutzen über eine Milliarde Menschen auf der Welt das Internet. Millionen von Computern sind mit diesem Netzwerk verbunden. Programme interagieren miteinander und über ihren Webbrowser können Sie durch das Netz surfen. Doch wie kommunizieren diese Programme über das Internet? Obwohl die Antwort abhängig von der Anwendung und dem Betriebssystem selbst ist, kann man mit großer Sicherheit sagen dass sie eine Socket-API verwenden. Die Grundlagen der Netzwerkprogrammierung mit TCP/IP und der Socket-API werden in dem Artikel durch Protokollgrundlagen und einschlägige Implementierungsmuster für typische Client-Server-Programme vertieft.

Dieser Artikel wird Ihnen einen Einblick in die TCP/IP Socket-Programmierung mit der Programmiersprache C# gewähren. Das Dokument behandelt die unabdingbaren Grundlagen, soll aber nicht die Funktionsweise des Internets selbst und dessen Protokolle im Detail erläutern. Das würde mit Sicherheit den Rahmen sprengen. Deshalb sei hier auf den technischen Artikel „Was ist TCP/IP?“ verwiesen, der Ihnen einige Grundlagen vermittelt und einen Animationsfilm bereitstellt. Ausführliche und fundierte Kenntnisse erhalten Sie unter anderem über die folgenden Lektüren: TCP/IP von W. Richard Stevens, TCP/IP Illustrated (englisch) und The TCP/IP Guide (englisch). Das zuletzt genannte Buch steht kostenlos als Online-Referenz auf der gleichnamigen Seite zur Verfügung.

Zielgruppe

Es wird vorausgesetzt, dass Sie sich mit der Programmiersprache C# und mit ihrer grundlegenden Syntax auskennen. Auch sollten Sie in der Lage sein die gelisteten Quellcodes zu kompilieren. Ziel des Artikels ist es, dem Programmierer ein fundiertes Wissen auf dem Gebiet der Netzwerkprogrammierung zu vermitteln, welches ihn in die Lage versetzt eigene Netzwerkanwendungen zu entwickeln. Er richtet sich an Programmierer und Softwareentwickler, die zum ersten Mal in Kontakt mit der Netzwerkprogrammierung kommen oder ihre rudimentären Kenntnisse auf diesem Gebiet vertiefen möchten. Der Artikel vermittelt Grundlagen ohne dabei auf abstrakte plattformspezifische Technologien, wie die Windows Communication Foundation (WCF), einzugehen. Am Ende dieses Tutorials finden Sie eine entsprechende Microsoft Visual Studio Projektmappe, die sämtliche Codebeispiele aus diesem Artikel und viele weitere Programme beinhaltet.

Neben C# sollte Ihnen auch das .NET Framework vertraut sein und der Begriff TCP nicht gänzlich unbekannt. Unabhängig davon werden im Dokument die erforderlichen theoretischen Grundlagen zum Internet und TCP/IP behandelt. Alle Beispielcodes beschränken sich auf die Konsole, da zusätzlicher Code für die GUI von den elementaren Codezeilen ablenken würde und nur unnötiger Ballast wäre. In der VS Projektmappe im Tutorial-Anhang finden Sie dennoch auch Windows Applikationen in der gewohnten Fensteransicht. Das vorliegende Dokument bezieht sich auf C# 3.0 und das Framework 4.0. Die verwendete Entwicklungsumgebung ist Visual Studio 2010.

Grundlagen zum Netzwerk, zu Paketen und Protokollen

Ein Computer-Netzwerk besteht aus Maschinen die verbunden sind über so genannte Kommunikationskanäle um Daten auszutauschen. Wir unterteilen diese Maschinen in Hosts und Router. Ihr Computer dürfte in aller Regel ein Host sein, denn auf ihm laufen Anwendungen, wie z.B. ein Internet-Browser. Im Gegensatz zum Host besteht die Aufgabe der Router darin Informationen durch das Netzwerk zu leiten. Daher die englische Bezeichnung Router, welches von routen also leiten abstammt. Doch weshalb brauchen wir diese Router? Warum können die Hosts ihre Daten nicht einfach direkt untereinander austauschen? Nun ja, das wäre eine äußerst impraktikable Lösung. Man müsste Millionen von Computern direkt miteinander verbinden. Nein, stattdessen verbinden sich einige Computer mit einem einzelnen Router welcher seinerseits wiederum mit anderen Routern verbunden ist. Auf diese Weise entsteht ein gut skalierbares Netzwerk welches mit verhältnismäßig wenigen Kommunikationskanälen auskommt. Das folgende vereinfachte Schema zeigt den prinzipiellen Aufbau dieses Netzwerks:

network structure

Das Internet ist ein so genanntes Teilstrecken-Netzwerk. Die Kommunikation damit geschieht nicht verbindungsorientiert, wie ein Telefonat, sondern paketorientiert. Das bedeutet, dass der Datenstrom in kleine Pakete aufgeteilt wird. Um die einzelnen Informationspakete erfolgreich über das Netzwerk transferieren zu können ist ein vereinheitlichendes Protokoll notwendig. Ein Netzwerkprotokoll (auch Netzprotokoll, Übertragungsprotokoll) ist eine exakte Vereinbarung, nach der Daten zwischen Computern ausgetauscht werden, die durch ein Netz miteinander verbunden sind. Die Vereinbarung besteht aus einem Satz von Regeln und Formaten, die das Kommunikationsverhalten der kommunizierenden Instanzen in den Computern bestimmen. Ein Protokoll ist vergleichbar mit einer Sprache, die eine Syntax und Semantik besitzt. Um kommunizieren zu können, muss die Anwendung in der Lage sein das erforderliche Protokoll zu verstehen. Viele Protokolle nutzen andere Protokolle, ähnlich wie man in manchen Sprachen Bestandteile anderer Sprachen finden kann.
Für das Internet ist das TCP/IP von eminenter Bedeutung. Es handelt sich dabei um eine ganze Familie von Netzwerkprotokollen. Ursprünglich wurde TCP als monolithisches Netzwerkprotokoll entwickelt, jedoch später in die Protokolle IP und TCP aufgeteilt. Das Internet Protokoll (IP) ist in der Vermittlungsschicht angesiedelt und für die Weitervermittlung von Paketen und die Wegewahl (Routing) zuständig. Das Transmission Control Protocol (TCP) baut auf dem IP auf und stellt eine Ende-zu-Ende-Verbindung her. Es ist Bestandteil der Transportschicht im OSI-Schichtenmodell. Neben TCP und IP ist auch das User Datagram Protocol (UDP) Teil dieser wichtigen Protokollfamilie. Im Unterschied zu TCP ist das UDP nur ein äußerst minimales, verbindungsloses Transportprotokoll welches nicht auf Zuverlässigkeit ausgelegt ist. Eingebettet als so genannte Layer (Schichten) bilden diese Protokolle eine Internetprotokollfamilie, die zusammen mit weiteren Protokollen die Basis für die Netzkommunikation im Internet bilden.

Unter TCP/IP und dem Internet Protokoll Version 4 (IPv4) wird eine Maschine anhand einer binären 32-Bit (4-Byte) Nummer identifiziert. Diese IP-Adresse ist sinngemäß vergleichbar mit der Adresse auf Ihren Briefumschlägen. Alle Computer, die mit dem Internet verbunden sind, erhalten automatisch eine individuelle IP-Adresse. IPv4 bietet einen Adressraum von etwas über vier Milliarden IP-Adressen, mit denen Computer und andere Geräte angesprochen werden können. In den Anfangstagen des Internet, als es nur wenige Rechner gab die eine IP-Adresse brauchten, galt dies als weit mehr als ausreichend. Aufgrund des unvorhergesehenen Wachstums des Internet und dem Einzug von mobilen Geräten, wie internetfähigen Smartphones, herrscht heute Adressenknappheit und so wurde das Internet Protokoll Version 6 (IPv6) mit einem deutlich größeren Adressraum von 128-Bit entwickelt. Wir werden uns deshalb in diesem Tutorial mit beiden Protokollversionen befassen müssen, sowohl mit der gängigen Internet Protokoll Version 4, als auch mit der neuen Version 6, mit einem Adressbereich von 128-Bit. Das .NET Framework implementiert seit der Framework Version 2.0 aber auch die Version 6 des Internet Protokolls. Die meisten Netzwerke und damit auch das Internet befinden sich heute in einem Umbruch, hin zu IPv6. Dieser Umbruch gestaltet sich sehr langsam und so wird auch heute noch vielerorts das Internet Protokoll in der Version 4 verwendet.

Neben der IP-Adresse spielt auch die Port-Nummer eine entscheidende Rolle. Mit Hilfe der Port-Nummer ist es möglich neben der Adresse selbst den Bereich näher einzugrenzen. Identifiziert die IP-Adresse das Gebäude selbst, so ordnet die Port-Nummer das Paket einem bestimmten Raum zu. Im Netzwerk sind die Räume stellvertretend für die Anwendungen die auf einem Host laufen. Dabei kann es sich beispielsweise um ihren Browser handeln. Die Port-Nummern bestehen im Gegensatz zu IP-Adressen nur aus 16-Bit und liegen dementsprechend in einem Integer Bereich zwischen 1 und 65535, wobei die 0 bereits reserviert ist.

Domain Name System (DNS)

Da der Mensch sich die im Internet Protokoll spezifizierten Adressen, z.B. 64.233.167.99, in aller Regel relativ schlecht einprägen kann und diese Adressen aufgrund der Netztopografie fluktuieren können, wurde ein Mechanismus entwickelt der Domainnamen direkt in die zugehörige IP-Adresse und weitere Informationen abbildet. Dies ist vergleichbar mit einem Telefonbuch, das die Namen der Teilnehmer in ihre Telefonnummer auflöst. So ist es möglich das Sie einen Namen, bestehend aus lateinischen Buchstaben, Ziffern und dem Bindestrich, in die Browserleiste eintippen und dieser automatisch, in die vom TCP/IP definierte Adresse transformiert wird. Dieses System, nämlich das Domain Name System, kurz DNS, ist eine Art Datenbank die für jede Domain die zugehörigen Protokolldaten gespeichert hat. Die hierarchische Datenbank ist weltweit auf tausende Server verteilt, die den Namensraum des Internets vollständig verwalten. Ändert sich beispielsweise die IP-Adresse von www.codeplanet.eu, so mappt das DNS den Namen automatisch auf die neue IP-Adresse. Ihr Browser kann die aktuelle IP-Adresse direkt über den Domainnamen bei einem DNS-Server abfragen.

Für die Verbindung zu einem DNS-Server, der es Ihnen ermöglicht in bekannter Weise im Internet zu surfen, benötigt ihr Browser zunächst ebenfalls eine IP-Adresse, nämlich die des DNS-Servers selbst. Diese wichtige IP-Adresse wird Ihrem System bei der Einwahl in das Internet automatisch vom Provider mitgeteilt. Die Adresse lässt sich unter Windows den Netzwerkverbindungsdetails entnehmen. Ihr Browser sendet summa summarum nach Eingabe eines Domainnamens zuerst eine Anfrage an einen DNS-Server, bekommt anschließend die IP-Adresse, die zu diesem Domainnamen gehört zugeschickt und kann dann die angeforderte Seite aufrufen. Als gewöhnlicher Internetnutzer brauchen Sie sich darum keine Gedanken zu machen, das alles erledigt Ihr Browser vollautomatisch im Hintergrund.

dns-resolve

Clienten und Server

Zur Übermittlung von Informationen über einen Kommunikationskanal sind zwei Stellen erforderlich. Zum Einen der Sender der den Kommunikationskanal öffnet indem er eine Gegenstelle anruft und zum Anderen der Empfänger der diesen Anruf entgegennimmt und antwortet. Ähnlich wie bei einem Telefongespräch differenziert man auch in der Netzwerkkommunikation und unterteilt die an der Kommunikation beteiligten Stellen, nämlich in den Client und den Server. Der Client (engl. Bez. für Klient) initiiert die Kommunikation während der Server auf Anfragen wartet und dann dem Clienten antwortet. Entscheidend hierbei ist das der Client die Adresse des Servers kennen muss, nicht aber umgekehrt. Ob ein Programm auf ihrem Rechner als Client oder als Server agiert entscheidet darüber wie das Programm die Socket API verwendet, um mit dem so genannten Peer zu kommunizieren. Für einen Server ist der Peer der Client und vice versa.

In einer echten Peer-To-Peer-Anwendung ist der Client auch gleichzeitig der Server. Bekannte P2P Programme, wie z.B. eMule senden und empfangen Daten über das Netzwerk und sind deshalb Client und Server zugleich. Dies wird meist mithilfe von Threads realisiert. Während sich der eine Thread um eingehende Anforderungen kümmert (agiert als Server), sendet ein anderer Thread ausgehende Verbindungsanforderungen (agiert als Client).

Neben der IP-Adresse des Servers, die der Client unbedingt kennen muss, fällt dem Port ebenfalls eine wichtige Rolle für eine geregelte Kommunikation über TCP zu. Die IANA weist deshalb bestimmten Diensten jeweils fest definierte Ports zu. So sind gängige Ports, wie beispielsweise Port 21 (FTP) und 80 (HTTP) bereits reserviert. Dennoch können sich Client und Server entsprechend arrangieren, so dass prinzipiell jede Port-Nummer verwendet werden kann. Dies setzt allerdings voraus, dass der Client die Port-Nummer einer bestimmten Anwendung zuordnen kann.

Was ist ein Socket?

Schlägt man im Lexikon nach so findet man folgende Erklärung:

Definition: Ein Socket ist ein Endpunkt einer bi-direktionalen Software-Schnittstelle zur Interprozess- (IPC) oder Netzwerk-Kommunikation zwischen zwei Programmen. Ein Socket ist gebunden an eine Port-Nummer, so dass die TCP Schicht die Anwendung identifizieren kann für die die Informationen bestimmt sind.

Was heißt das nun genau? Ein Socket kann man sich als "Steckdose" vorstellen. Maschinen können über diese Steckdose Zugang zum Netz erhalten. Im Prinzip ist ein Socket eine Abstraktion die es einer Anwendung ermöglicht sich in ein bestehendes Netzwerk einzuklinken um mit anderen Applikationen zu kommunizieren. Ein Socket kann wie eine Datei geöffnet, beschrieben, gelesen und geschlossen werden. Sockets bilden eine standardisierte Schnittstelle (API) zwischen der Netzwerk-Protokoll-Implementierung des Betriebssystems und der eigentlichen Applikation. Die erste Socket Implementierung wurde auf dem Betriebssystem FreeBSD vorgenommen und bekannt unter dem Namen Berkeley Sockets API.

Sockets unterscheiden sich je nachdem welcher Protokoll Suite sie angehören. Die wesentlichen Socket Typen für das TCP/IP sind Stream Sockets und Datagram Sockets. Stream Sockets kommunizieren über einen Zeichen-Datenstrom; Datagram Sockets über einzelne Nachrichten. Während Stream Sockets das Transmission Control Protocol verwenden, nutzen Datagram Sockets das User Datagram Protocol. Das nachfolgende Abbild illustriert sowohl TCP- als auch UDP-Sockets.

sockets protocol layer

Wie man anhand des Diagramms erkennen kann ist jeder Port einem bestimmten Socket des Hosts zugeordnet. Die auf dem Host laufenden Applikationen können auch auf denselben Socket zugreifen! Sockets setzen auf die Protokolle einer Transportschicht wie TCP oder UDP auf. Aus Sicht der Applikation ist ein Socket nichts anderes als ein Zugang zum Netzwerk bzw. Internet.

Sockets im .NET Framework

Das .NET Framework implementiert die klassische Windows Socket API, auch WinSock genannt und umschließt die C Implementierung zu einer höheren Abstraktionsschicht. Dieses hohe Maß an Abstraktion vereinfacht viele Programmierschritte, ist allerdings auch oft zu unflexibel. Microsoft stellt deshalb zwei Schichten im .NET Framework zur Verfügung. Die auf der WinSock Schicht aufliegende Socket Klasse, die im Prinzip alle wesentlichen Funktionen von WinSock 2.0 implementiert und zusätzlich auf dieser eine weitere Schicht die die höheren Socket Klassen, nämlich TcpClient, TcpListener und UdpClient bereitstellt.

Die wesentlichen Klassen zur Netzwerkprogrammierung sind in den Namespaces System.Net und System.Net.Sockets der .NET Klassenbibliothek zu finden. System.Net stellt eine einfache verwaltete API für viele Protokolle die von den heutigen Netzwerken verwendet werden zur Verfügung. Darunter befinden sich Klassen, wie Dns zur Auflösung von Adressen, Cookie für die einfache Handhabung von Cookies und IPAddress zur Manipulation von IP-Adressen. Im Namespace System.Net.Sockets befindet sich mitunter die Klasse Socket, welche die Berkeley Socket API implementiert sowie auch TcpClient für Client Verbindungen bei TCP Netzwerk Diensten. Das .NET Framework unterscheidet deutlich zwischen TCP und UDP indem es für beide Protokolle separate Klassen zur Verfügung stellt.

.NET und das Domain Name System

Wie wir in einem vorhergehenden Abschnitt erfahren haben, werden Domainnamen über das Domain Name System in die entsprechende IP-Adresse übersetzt und vice versa. Das .NET Framework stellt hierfür die Klasse Dns bereit, welche wiederum zahlreiche Methoden zur Auflösung der IP-Adresse und des Hostnamen zur Verfügung stellt. Sehen wir uns deshalb nun zunächst einmal ein vollständiges Kommandozeilenprogramm an um die Methoden der Klassen Dns und IPAddress nachzuvollziehen.

using System;
using System.Net;
using System.Net.Sockets;
 
namespace CodePlanet.Articles.ProgrammingSockets
{
    /// <summary>
    /// Das folgende Kommandozeilenprogramm illustriert einige
    /// Methoden der .NET Framework Klassen Dns und IPAddress.
    /// </summary>
    public class MyHost
    {
        public static void Main(string[] args)
        {
            // Gibt die localhost Daten aus
            try {
                Console.WriteLine("Local Host:\n");
                // Die Methode Dns.GetHostName() gibt den Namen der lokalen Maschine zurück 
                String localHostName = Dns.GetHostName();
                Console.WriteLine("\tHost Name: " + localHostName);
                PrintHostInfo(localHostName);
            } catch (Exception e) {
                Console.WriteLine(e.Message + "\n");
            }
 
            // Falls Argumente übergeben wurden, gebe die Host Informationen aus
            if (args.Length > 0) {
                foreach (String arg in args) {
                    Console.WriteLine(arg + ":");
                    PrintHostInfo(arg);
                }
            }
        }
 
        // Statische Helper-Funktion
        static void PrintHostInfo(String host)
        {
            try {
                IPHostEntry hostInfo;
 
                // Versuche die DNS für die übergebenen Host und IP-Adressen aufzulösen
                hostInfo = Dns.GetHostEntry(host);
 
                // Ausgabe des kanonischen Namens
                Console.WriteLine("\tCanonical Name: " + hostInfo.HostName);
 
                // Ausgabe der IP-Adressen
                Console.Write("\tIP Addresses: ");
 
                foreach (IPAddress ipaddr in hostInfo.AddressList) {
                    Console.Write(ipaddr.ToString() + " ");
                }
 
                // Ausgabe der Alias-Namen für diesen Host
                Console.Write("\n\tAliases: ");
 
                foreach (String alias in hostInfo.Aliases) {
                    Console.Write(alias + " ");
                }
 
                Console.WriteLine("\n");
            } catch (Exception) {
                Console.WriteLine("\tUnable to resolve host: " + host + "\n");
            }
        }
    }
}

Neben dem eigentlichen Namen des Hosts, dem so genannten CNAME (engl. für canonical name) werden auch eventuelle Alias-Namen ausgegeben. Mit Einführung des .NET Framework 2.0 wurde die ursprüngliche Methode Resolve() der Klasse Dns als obsolet (veraltet) markiert. Abgelöst wird die Methode durch die Methode GetHostEntry(). Die Methode GetHostEntry() ist zweifach überladen und löst den Host-Namen oder die IP-Adresse zu einer IPHostEntry Instanz auf. Bei IPHostEntry handelt es sich um eine Container Klasse die von einigen Dns Methoden zurückgegeben wird. Anschließend werden zwei Eigenschaften von IPHostEntry ausgenutzt um eine Liste mit IP-Adressen und Alias-Namen auszugeben. Schlägt die Auflösung zuvor fehl so wird eine entsprechende Exception ausgelöst.

Unser kleines Programm beinhaltet bereits einige Klassen und Methoden die in der Netzwerkprogrammierung von Bedeutung sind. Von Sockets ist aber weit und breit nichts zu sehen. Der Grund dafür liegt darin begründet, dass die Methoden der Klasse Dns ihr Zusammenspiel mit der .NET Socket-Klasse verbergen. Lassen Sie uns nun endlich den Kern jeder Netzwerkprogrammierung kennenlernen und einen detaillierten Blick auf die .NET Socket-Klasse werfen.

Die .NET Socket-Klasse

Da es sich bei System.Net.Sockets.Socket um einen Wrapper um die Berkeley Sockets Schnittstelle handelt, deckt die Klasse nahezu alle Funktionalitäten für die Socket-Programmierung ab. Die Komplexität der WinSock API ist aufgrund ihres Umfangs relativ hoch. Wir beschränken uns daher zunächst einmal auf das Senden und Empfangen von Daten. Doch bevor wir Daten senden und empfangen können müssen wir wissen wie wir ein Socket Objekt erstellen können?

Die Socket Klasse stellt einen Konstruktor zur Verfügung um eine neue Socket Objekt-Instanz zu initialisieren. Die Parameterliste sieht wie folgt aus:

public Socket(
   AddressFamily addressFamily,
   SocketType socketType,
   ProtocolType protocolType
);

Um ein Socket Objekt zu instanzieren müssen 3 Argumente (allesamt Enumerationen) übergeben werden.

  • Die AddressFamily spezifiziert das Adressierungschema das eine Instanz der Socket Klasse benutzen kann. Das kann z.B. InterNetwork sein welches das IPv4 implementiert oder InterNetworkV6 für IPv6 oder Unix für Unix Adressen.
  • Als zweiter Parameter wird der SocketType übergeben. Die häufig verwendeten Typen sind die Stream und Datagram Sockets. Aber auch Raw Sockets kommen oft zum Einsatz.
  • Der dritte und letzte Parameter ist ProtocolType und spezifiziert den verwendeten Protokolltyp. Mögliche Argumente sind unter anderem das TCP, UDP oder IP.

Wir erzeugen nun einmal einen Stream Socket. Wie wir wissen verwenden Stream Sockets das Transmission Control Protocol (TCP) welches auf dem Internetprotokoll (IP) aufbaut. Unser Konstruktoraufruf sieht dann so aus:

Socket sock = new Socket(AddressFamily.InterNetwork, 
                         SocketType.Stream, 
                         ProtocolType.Tcp);

Nachdem wir eine Socket Instanz erzeugt haben können wir zu einem entfernten Server verbinden indem wir die Connect() Methode der Socket Klasse aufrufen. Doch bevor wir einen neuen Kommunikationskanal etablieren können erinnern wir uns an die dafür notwendigen Daten. Erinnern Sie sich? Exakt, wir benötigen eine IP-Adresse und eine Port-Nummer. Wir bedienen uns deshalb einer Klasse im Namensraum System.Net, nämlich der Klasse IPEndPoint. Diese repräsentiert einen Netzwerk Endpunkt bestehend aus einer IP-Adresse und einer Port-Nummer. Die Klasse stellt zwei Konstruktoren zur Verfügung:

public IPEndPoint(long, int);
public IPEndPoint(IPAddress, int);

Im ersten Konstruktor wird die IP-Adresse als long Typ (__int64) übergeben. Der C# Datentyp long, intern klassifiziert als System.Int64, umfasst 8 Byte.

Anmerkung: Aufgrund des bevorstehenden langsamen Wandels des Internet Protokolls von Version 4 auf Version 6 hat Microsoft vorgesorgt und mit dem Release des .NET Frameworks 2.0 IPv6 eingeführt.

Sie fragen sich in diesem Augenblick vielleicht was passieren würde wenn wir statt InterNetwork das InterNetworkV6 in der AddressFamily angeben würden? Wie kann dann ein 128-bit großer Wert einer IPv6 Adresse als Argument übergeben werden? Die Konstruktoren der Klasse IPEndPoint akzeptieren doch nur 64-bit?! Sie haben recht. Die IPv6 Adressen umfassen 128-bit also 2^128 Adressen. Jedoch dienen nur die ersten 64 Bit der IPv6-Adresse üblicherweise der Netzadressierung. Entspricht die Adresse der so genannten hexadezimalen Notation mit Doppelpunkten (engl. colon-hexadecimal notation) so sind nur die ersten 64 Bit erforderlich. Beispielsweise wären das bei der IPv6 Adresse 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 dann die vier 16-bit Gruppen 3FFE:FFFF:7654:FEDA.

Zurück zur Internet Protokoll Version 4 deren Adressen "nur" 32-bit umfassen. Eine Adresse als long Typ, z.B. die IP-Adresse 3232235535, zu übergeben ist in der Praxis nicht üblich. Aufgrunddessen wird normalerweise der zweite Konstruktor aufgerufen. Dieser Konstruktor erwartet ein Objekt der Klasse IPAddress. In der Regel wird eine neue Instanz der Klasse IPAddress über die Methode Parse() aufgerufen. Damit ist es möglich eine IP-Adresse, die aus 4 Oktetts in der Form xxx.xxx.xxx.xxx besteht, als Argument vom Typ string zu übergeben. Das ist die allgemein gängige Methode eine IP-Adresse anzugeben. Die oben genannte IP-Adresse vom Typ long geschrieben in der "dotted-quad notation" ist 192.168.0.16.

Beide Konstruktoren erwarten als zweites Argument einen Integer. Dabei handelt es sich um die Port-Nummer. Sie finden die Port-Nummern für verschiedene Dienste unter anderem auf Wikipedia unter dem Stichwort Port (Protokoll).

Im folgenden Beispiel ist uns die IP-Adresse bekannt. Wir nutzen die Methode Parse() und wollen zu einem Webserver Verbindung aufnehmen. Dazu benutzen wir das bereits zuvor instanziierte Socket Objekt sock:

const int Port = 80;
const string IPv4 = "232.128.10.1";
 
IPAddress ipo = IPAddress.Parse(IPv4);
IPEndPoint ipEo = new IPEndPoint(ipo, Port);
 
// Konstruktor: 
// public void Connect(
//    EndPoint remoteEP
// );
sock.Connect(ipEo);

Nachdem wir mit Hilfe der IP-Adresse und der Port-Nummer ein neues Objekt der Klasse System.Net.IPEndPoint erzeugt haben können wir dieses an die Socket Methode Connect() übergeben. Die Verbindung wird aufgebaut.

Die IP-Adressen von Internetseiten bekommt der Nutzer nur selten zu sehen. Stattdessen nutzt man die Domain Namen. In unserem ersten Programm haben wir einige Methoden und Klassen zur Auflösung lokaler Adressen verwendet. Darunter System.Net.Dns und System.Net.IPHostEntry. Die Klasse Dns empfängt Informationen zu den gegebenen Hosts vom Internet Domain Name System (DNS) um so an die entsprechend zugeordneten IP-Adressen zu gelangen. Lassen Sie uns nun die gewonnenen Informationen kombinieren um die Domain codeplanet.eu aufzulösen und anschließend eine Verbindung herzustellen. Zuvor geben wir die IP-Adressen auf die Konsole aus.

using System;
using System.Net;
using System.Net.Sockets;
 
namespace CodePlanet.Articles.ProgrammingSockets
{
    /// <summary>
    /// Das folgende Programm löst die übergebene Domain auf, stellt
    /// eine Verbindung zu dieser her und schließt diese dann wieder.
    /// </summary>
    public class DomainTests
    {
        static void Main(string[] args)
        {
            try {
                Socket sock = null;
                string host = "codeplanet.eu";  // Uri
                int port = 80;
 
                IPHostEntry hostEntry = Dns.GetHostEntry(host);
                IPAddress[] ipAddresses = hostEntry.AddressList;
 
                Console.WriteLine(host + " is mapped to the IP-Address(es): ");
 
                // Ausgabe der zugeordneten IP-Adressen
                foreach (IPAddress ipAddress in ipAddresses) {
                    Console.Write(ipAddress.ToString());
                }
 
                // Instanziere einen Endpunkt mit der ersten IP-Adresse
                IPEndPoint ipEo = new IPEndPoint(ipAddresses[0], port);
 
                // IPv4 oder IPv6, Stream Socket, TCP
                sock = new Socket(ipEo.AddressFamily,
                                  SocketType.Stream,
                                  ProtocolType.Tcp);
 
                // Öffne eine Socket Verbindung
                sock.Connect(ipEo);
 
                // Prüfe ob eine Verbindung besteht?
                if (sock.Connected) {
                    Console.WriteLine(" - Connection established!\n");
                    // Socket schließen und Ressourcen freigeben
                    sock.Close();
                }
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }
    }
}

Die Funktionsweise des Programms dürfte bekannt sein. Lediglich zwei kleine Änderungen haben sich eingeschlichen. Wir geben im Konstrukturaufruf des Sockets nicht mehr explizit die Adressfamilie an sondern nutzen stattdessen die Eigenschaft AddressFamily der Klasse IPEndPoint. Diese liefert implizit die entsprechende Adressfamilie zurück zu der dieser Endpunkt gehört. Auf diese Weise lässt sich eine Ausnahme in Folge einer inkompatiblen Adressfamilie vermeiden. Schließlich könnte es sein das Dns.GetHostEntry() eine IPv6 Adresse zurückgibt.

Seit dem Service Pack 2 von Windows Vista gibt die Methode GetHostEntry nur noch die Adressfamilie InterNetworkV6 bei lokalen Adressen zurück, so dass es bei falschem Socketaufruf zu der Ausnahme "Es wurde eine Adresse verwendet, die mit dem angeforderten Protokoll nicht kompatibel ist" kommt. Ändern Sie die Adressfamilie in diesem Fall explizit in InterNetworkV6.

Zusätzlich nutzen wir eine weitere Eigenschaft. Diesmal eine der Klasse Socket, nämlich Connected. Diese liefert den boolschen Wert true zurück falls eine Verbindung etabliert wurde. Andernfalls wird false zurückgegeben.



Zuletzt aktualisiert am Donnerstag, den 02. Januar 2014 um 23:03 Uhr
 
AUSWAHLMENÜ