Netzwerk-Know-how (tecCHANNEL COMPACT) Kapitel 8: Verbindungsorientierte Kommunikation

Veröffentlicht: 11. Jul 2005

Von PROF. DR. STEPHAN EULER

Eine elementare Schnittstelle zur Programmierung von Netzwerkanwendungen bieten die so genannten Sockets (Englisch für Steckdose). Als API (Application Program Interface) stellen Sockets Funktionen zum Aufbau von Verbindungen sowie zur Kommunikation zwischen Applikationen bereit.

Auf dieser Seite

In diesem Beitrag

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Verbindungsorientierte Kommunikation

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „socket"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „bind"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „listen"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „accept"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „send" und „recv"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Einfacher Server

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Funktion „connect"

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Verbindungslose Kommunikation

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Realisierung in Perl

Dn151202.ACDCF196BC98A92A7E35715F19C8C405(de-de,TechNet.10).png Literatur

Sockets kapseln die Details der Netzwerkkommunikation. Der Programmierer kann sich so auf die Realisierung der Anwendung konzentrieren. Im Idealfall funktionieren die Anwendungen dadurch unabhängig von Programmiersprache, Betriebssystem und Netzwerk-Interface.

Das Socket API wird von allen gängigen Betriebssystemen unterstützt. Sockets setzen auf die Protokolle einer Transportschicht wie TCP oder UDP auf. Aus Sicht der Anwendung ist ein Socket ein Zugang zum Netzwerk. Im Folgenden diskutieren wir die Programmierung am Beispiel der Programmiersprache C. Die Verwendung in anderen Programmiersprachen erfolgt ähnlich. Am Ende dieses Beitrages zeigen wir eine Implementierung in der Sprache Perl.

Das Socket API stellt zwei Verfahren zur Verfügung: Verbindungsorientierte und verbindungslose Kommunikation. Im ersten Fall müssen die beiden beteiligten Rechner zuerst eine Verbindung aufbauen, bevor sie Daten austauschen können. In beiden Fällen jedoch übernimmt einer der beiden Rechner - der Server - die Aufgabe, zunächst die Netzressource aufzubauen. Anschließend wartet er auf Verbindungswünsche des anderen Rechners - des Clients. Ein Server kann im Allgemeinen mehrere Clients bedienen. Sockets eignen sich nicht nur für die Kommunikation zwischen Rechnern. Sie können auch die Kommunikation zwischen mehreren Applikationen auf einem Rechner erledigen.

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).png Zum Seitenanfang

Verbindungsorientierte Kommunikation

Den prinzipiellen Ablauf des Auf baus einer Verbindung und des anschließenden Datenaustausches zeigt die Tabelle auf der nächsten Seite. Für jeden Schritt ist der Name der entsprechenden Funktion in der Programmiersprache C angegeben. Eine Besonderheit ist bei der Verwendung von Sockets unter Windows (Win-dows-Sockets) zu beachten: Um den Service benutzen zu können, muss zunächst die Funktion WSAStartup aufgerufen werden. Dabei kann der Programmierer die gewünschte Version einstellen. Dazu ein Beispiel:

> long WinsockStartup()
> {
> long rc;
> 
> WORD wVersionRequested;
> WSADATA wsaData;
> wVersionRequested = MAKEWORD(2, 1);
> 
> rc = WSAStartup( wVersionRequested, &wsaData );
> return rc;
> }

In diesem Beispiel wird die Version 2.1 spezifiziert. Nach dem Aufruf stehen in der Struktur wsaData Informationen über die gefundene Implementierung der Windows-Sockets.

Dn151202.5F88A80EEE294AA4A76834A579762F8E(de-de,TechNet.10).png

Tabelle 9.1: Funktionen für verbindungsorientierte Sockets

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „socket"

Zu Beginn muss man das zu verwendende Protokoll spezifizieren. Dies erfolgt mit der Funktion

> SOCKET socket( int family, int type, int protocol );

Das erste Argument spezifiziert die Protokollfamilie (Adressfamilie). Mögliche Werte sind unter anderem:

  • AFJJNIX Unix-internes Protokoll
  • AFJNET Internet-Protokoll IPv4
  • AF_INET6 Internet-Protokoll IPv6
  • AF_APPLETALKAppleTalk

Diese Werte sind als Konstanten in entsprechenden Header-Dateien definiert. Das Präfix AF_ steht für Adressfamilie. Gleichwertig dazu existieren die Konstanten mit den gleichen Endungen und dem Präfix PF_ für Protokollfamilie. Das zweite Argument type gibt die Verbindungsart an. Die wichtigsten Möglichkeiten sind:

  • SOCK_STREAM Stromorientierte Verbindung
  • SOCK_DGRAM Datagrammverbindung

Eine weitere Option ist SOCK_RAW. Damit werden beispielsweise im Programm Ping ICMP-Pakete über einen Socket versandt. Das Argument protocol wird in den meisten Fällen auf 0 (NULL) gesetzt. Dann wird automatisch aus den beiden anderen Argumenten das passende Protokoll ausgewählt. So resultiert aus der Kombination AFJNET mit SOCK_STREAM eine TCP-Verbindung. AF_INET mit SOCK_DGRAM definiert eine UDP-Verbindung. Ansonsten kann man auch explizit ein Protokoll auswählen (z.B.IPPROTO_TCP).

Bei Erfolg liefert die Funktion einen Bezeichner für einen Socket zurück. Eigentlich handelt es sich lediglich um einen Integer-Wert - sozusagen um den Index des Sockets. Zur besseren Lesbarkeit des Programms kann man den Datentyp SOCKET verwenden. Das folgende Codefragment zeigt beispielhaft einen Aufruf, um einen Socket für TCP zu erzeugen:

> SOCKET sockListen;
> sockListen=socket(AF_INET,SOCK_STREAM,NULL);
> if (sockListen == INVALID_SOCKET) {
> rintf("Error: Cannot create Socket\n");
> }

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „bind"

Im nächsten Schritt muss der Server dem Socket einen Namen geben, unter dem er später erreichbar sein wird. Dazu dient die Funktion bind in der Form

> int bind( SOCKET s, sockaddr *p_addr, int addrlen );

Das erste Argument ist der soeben angelegte Socket. Zur Übergabe der spezifischen Details wird eine Struktur SOCKADDR verwendet. Das zweite Argument besteht in einem Zeiger auf die Struktur, das dritte gibt deren Größe an. Für TCP verwendet man die Struktur SOCKADDR_IN mit folgenden Elementen

> struct sockaddr_in{
> short sin_family;
> unsigned short sin_port;
> struct in_addr sin_addr;
> char sin_zero[8];
> };

Das erste Element bestimmt die Adressfamilie. Das dritte Element ist wiederum eine Struktur, die die IP-Adresse enthält. Für unsere Zwecke reicht es aus, in dieser Unterstruktur ein Element auf INADDR_ANY zu setzen, um ein beliebiges Netzwerk-Interface an dem eigenen Rechner zu erlauben. Entscheidend ist das zweite Element sin_port. Hier wird die so genannte Portnummer eingetragen - der Name oder die Adresse für den Socket. Damit wird das System informiert, dass alle an diesem Port eingehenden Nachrichten an das Programm weitergereicht werden sollen. Bestimmte Dienste sind mit festen Portnummern verknüpft (zum Beispiel Webserver Port 80). Unter Unix können normale Benutzer nur Portnummern oberhalb von 1024 verwenden.

Bei der Angabe der Portnummer gilt es, die für das TCP-Protokoll passende Darstellung einer Integer-Zahl zu beachten. Verschiedene Systeme unterscheiden sich in der Reihenfolge von Low-Byte und High-Byte. Das Makro htons (Host to net-work, short) wandelt Integer-Werte vom Typ short entsprechend um. Genauer gesagt tauscht es, falls erforderlich, Low-Byte mit High-Byte. Mit diesen Bestandteilen kann man in folgender Weise die Portnummer 5432 an den Socket binden:

> #define SERVER_PORT 5432
> 
> long rc;
> SOCKADDR_IN addr;
> int addrlen = sizeof(addr);
> 
> addr.sin_family = AF_INET;
> addr.sin_port = htons(SERVER_PORT);
> addr.sin_addr.s_addr = INADDR_ANY;
> 
> rc = bind(sockListen, (SOCKADDR*)&addr, sizeof(addr));
> if (rc == SOCKET_ERROR) {
> printf("Error: Cannot bind Socket\n");
> }

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „listen"

Der Socket ist jetzt angelegt und mit einer Adresse versehen. Nun kann dem System mitgeteilt werden, dass der Server bereit ist, Anfragen zu bearbeiten. Die Funktion dazu ist

> int listen( SOCKET sock, int backlog);

Das erste Argument ist wieder der Socket. Das zweite Argument gibt an, wie viele Anfragen die Warteschlange maximal halten soll. Ist der Server gerade beschäftigt, speichert die Warteschlange neu eintreffende Anfragen und gibt sie nach und nach zur Bearbeitung weiter.

Die Größe der Warteschlange ist durch die Systemressourcen beschränkt. Eine sinnvolle Spezifikation ist SOMAXCONN. Damit wird automatisch die Maximalzahl eingestellt. Der entsprechende Abschnitt im Programm lautet:

> rc = listen(sockListen, SOMAXCONN);
> if (rc == SOCKET_ERROR) {
> printf("Error: Cannot listen\n" );
> }

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „accept"

Wenn sich Anfragen in der Warteschlange befinden, können sie mit der Funktion accept angenommen werden. Die Definition lautet:

> SOCKET accept( SOCKET s, sockaddr *p_addr, int * p_len );

Die Funktion accept wartet auf eine Anfrage für den angegebenen Socket. Erfolgt die Anfrage, gibt die Funktion einen neuen (Verbindungs-) Socket zurück. Dieser neue Socket wird dann für den Datenaustausch genutzt. Der alte Socket steht dadurch weiterhin für die Annahme weiterer Verbindungen bereit. Eine verbreitete Technik unter UNIX ist, mit dem neuen Socket einen Kindprozess zu starten. Der Kindprozess führt die gewünschte Aufgabe aus, während der Elternprozess bereits wieder neue Anfragen annehmen kann [1].

Das zweite Argument enthält nach der Rückkehr aus der Funktion Informationen über den Client. Dazu füllt accept die Struktur, auf die der angegebene Zeiger deutet. Die Länge der Struktur wird an die im dritten Argument über Zeiger angegebene Stelle geschrieben. Beide Argumente können auch NULL sein. In diesem Fall wird keine Information über den Client übermittelt.

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „send" und „recv"

Über den Verbindungs-Socket können Client und Server Daten austauschen. Aus Sicht der Applikation ähnelt der Verbindungs-Socket sehr stark einer Dateischnittstelle. In der Tat kann man auch mit den Systemfunktionen read und write sowohl Dateien als auch Sockets lesen beziehungsweise beschreiben.

Eine andere Möglichkeit besteht in der Verwendung der beiden Funktionen recv und send:

> int recv( SOCKET s, char * buf, int len, int flags );
> int send( SOCKET s, char * buf, int len, int flags );

Diese Funktionen benutzen einen Puffer, um Daten zu übertragen. Bei recv spezifiziert len die Länge des Puffers. Demgegenüber wird bei send die Anzahl der zu übertragenden Zeichen mitgeteilt. Das vierte Argument übernimmt optionale Spezifikationen für die Ausführung. In den meisten Fällen ist der Standardwert 0 ausreichend. Beide Funktionen geben zurück, wie viele Werte sie tatsächlich empfangen beziehungsweise geschickt haben.

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Einfacher Server

Mit den beschriebenen Funktionen lässt sich bereits ein Server realisieren. Als Client dient im Beispiel zunächst die Anwendung telnet. Mit dem Befehl telnet IP-Adresse Portnummer wird eine Verbindung hergestellt. Ohne physikalisches Netzwerk kann man zum Test auf dem gleichen Rechner die Loopback-Adresse localhost (127.0.0.1) verwenden. In diesem Fall gibt die Netzwerkkarte die Nachrichten direkt zurück.

Der folgende Programmcode implementiert einen einfachen Server. Der Server wartet auf Clients, schickt bei Anmeldung eine kurze Begrüßung und schließt danach unmittelbar wieder die Verbindung. Die Funktion gethostname wird dabei verwendet, um den Namen des eigenen Rechners zu erfragen. Dieser Name wird dann in den Begrüßungstext eingebaut. Zu Gunsten einer besseren Übersicht verzichtet das Beispiel auf eine Fehlerbehandlung. Die Version enthält spezielle Aufrufe für Windows-Sockets. Die Socket-Funktionen stehen in der Bibliothek ws2_ 32.1ib. Diese muss beim Aufruf des Links zusätzlich mit angegeben sein.

> #include <stdio.h>
> #include <winsock2.h>
> 
> #define SERVER_PORT 1234
> long WinsockStartup();
> 
> void main()
> {
> long rc; /* Variable fuer Rueckgabewerte */
> 
> SOCKET sockListen;
> SOCKET sockConnected;
> SOCKADDR_IN addr;
> int addrlen = sizeof(addr;
> 
> char welcome[200];
> char hostname[200];
> 
> addr.sin_addr.s_addr = 0;
> addr.sin_family = AF_INET;
> addr.sin_port = htons(SERVER_PORT);
> 
> /* Nur unter Windows erforderlich */
> rc = WinsockStartup();
> 
> /* Willkommenstext erstellen */
> gethostname( hostname, sizeof hostname );
> sprintf( welcome, "Willkommen bei host %s \r\n", hostname);
> 
> /* Socket anlegen und aktivieren */
> sockListen=socket(AF_INET,SOCK_STREAM,NULL);
> bind(sockListen, (SOCKADDR*)&addr, sizeof(addr));
> listen(sockListen, SOMAXCONN);
> /* In Endlosschleife auf Clients warten
> * Begruessung schicken und
> * Verbindung wieder abbauen */
> for( ;; ) {
> printf( "Warten auf naechste Verbindung... \n" );
> sockConnected = accept( sockListen,
> (SOCKADDR*)&addr, &addrlen);
> rc = send( sockConnected,
> welcome, strlen(welcome) + 1, NULL);
> printf("%d Bytes geschickt\n", rc );
> 
> /* Windows Socket beenden */
> shutdown( sockConnected, SD_SEND );
> closesocket( sockConnected );
> }
> }
> 
> /* *************************************************
> * Starte Socket Service unter Windows
> */
> long WinsockStartup()
> {
> long rc;
> /* gewünschte Version */
> WORD wVersionRequested;
> /* Datenstruktur fuer Info ueber Version */
> WSADATA wsaData;
> wVersionRequested = MAKEWORD(2, 1); /* Short Wert aus 2 Bytes */
> rc = WSAStartup( wVersionRequested, &wsaData );
> return rc;
> }

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Funktion „connect"

Zur Realisierung eines eigenen Clients benötigt man noch die Funktion

> int connect( SOCKET s, sockaddr *p_addr, int addrlen );

Der Client kann unmittelbar einen Socket mittels connect verbinden. In diesem Fall erhält er automatisch vom System eine freie Portadresse. Bei Erfolg (Rückgabewert 0) steht dann bereits ein Verbindungs-Socket zur Verwendung mit recv und send bereit. Die notwendigen Informationen über den Server - IP-Adresse und Portnummer - stehen in der Struktur, auf die der Zeiger p_addr deutet.

Der folgende Code zeigt einen einfachen Client. Dieser Client sucht nach einem Server auf Port 1234 auf dem lokalen Rechner. Nach jedem Versuch wartet er 500 ms bis zum nächsten Versuch. Gelingt die Verbindung zu einem Server, so wird eine Nachricht gelesen und der Text ausgegeben.

> #include <stdio.h>
> #include <winsock2.h>
> 
> #define SERVER_PORT 1234
> #define RECV_BUF_MAXLEN 256
> 
> long WinsockStartup();
> 
> void main()
> {
> SOCKET sock;
> SOCKADDR_IN addr;
> long rc;
> char recvBuf[RECV_BUF_MAXLEN+1];
> char hostAdress[] = "127.0.0.1";
> 
> rc = WinsockStartup();
> 
> // socket anlegen
> 
> sock=socket(AF_INET,SOCK_STREAM,NULL);
> 
> /* Informationen fuer Verbindung */
> addr.sin_family = AF_INET;
> addr.sin_port = htons(SERVER_PORT);
> addr.sin_addr.s_addr = inet_addr(hostAdress);
> 
> // socket mit Server verbinden, endlos immer wieder probieren
> while(
> connect(sock, (SOCKADDR*)&addr, sizeof(SOCKADDR))
> == SOCKET_ERROR)
> {
> printf("."); // Lebenszeichen ausgeben
> Sleep( 500 ); // 500ms warten
> }
> // Server gefunden
> printf("Verbunden mit %s ...\n", hostAdress);
> // Nachricht abholen und ausgeben
> rc = recv(sock, recvBuf, RECV_BUF_MAXLEN, NULL);
> recvBuf[rc] = '\0';
> printf("%d Byte empfangen: %s\n", rc, recvBuf );
> }

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Verbindungslose Kommunikation

Für eine verbindungslose Kommunikation sind weniger Schritte notwendig, da der Aufbau der Verbindung entfällt. Auf Seiten des Servers wird ein Socket angelegt und mit einem Port verbunden.

Der Client legt ebenfalls einen Socket an, den er mit einem Port verknüpfen kann. In den meisten Fällen ist dies aber nicht notwendig, da die Portnummer vom System automatisch beim ersten Verschicken eines Pakets zugewiesen wird.

Da mit einem Socket jetzt kein festes Ziel verbunden ist, muss die Zieladresse bei jedem Paket explizit mit angegeben werden. Daher wird anstelle der Funktion send die Funktion sendto verwendet. Die entsprechende Funktion zum Lesen ist recvfrom. Mit dieser Funktion kann ein Programm - ähnlich wie bei der Funktion accept beschrieben - Informationen über den Absender abfragen. Allerdings spielt beim Lesen eines Pakets der Unterschied zwischen verbundenen und unverbunde-nen Sockets eine weniger große Rolle. Die Informationen über den Absender sind dabei optional. Wenn dafür kein Bedarf dafür besteht, kann man auch die schon bekannte Funktion recv verwenden.

Dn151202.AE5F4CCAEDB957B9A419FEAB8C40D8FD(de-de,TechNet.10).png

Tabelle 9.2: Funktionsaufrufe für verbindungslose Sockets

Der Ablauf soll wieder am Beispiel eines Programmpaares für Server und Client erläutert werden. Als minimale Funktionalität empfängt der Server Pakete und schickt jeweils eine Bestätigung an den Absender zurück. Damit eine Kommunikation überhaupt zu Stande kommen kann, verwendet der Server eine feste Portadresse. Der Client kann dann über den Rechnernamen und diese Portadresse ein Paket an den Server schicken. Mit diesem Paket werden die entsprechenden Adressen des Clients übermittelt. Daher kann der Client seine Portadresse frei wählen (oder vom System auswählen lassen). Der Server entnimmt die Informationen dem Paket und kann dann seine Bestätigung schicken. Für diesen Zweck stehen die beiden letzten Argumente der Funktion

> int recvfrom( SOCKET s, char * buf, int len, int flags,
> sockaddr *p_addr, int *p_len );

zur Verfügung. Das vorletzte Argument enthält einen Zeiger auf eine Struktur, die die Informationen aufnehmen soll. Die tatsächliche Länge wird in die durch den Zeiger p_len angegebene Stelle geschrieben. Das Beispielprogramm wertet die Struktur aus, um die Angaben über den Absender auszugeben. Anschließend sendet der Server mit der Funktion

> int sendto( SOCKET s, char * buf, int len, int flags,
> sockaddr *p_addr, int len );

eine Bestätigung (der Text OKAY). Bei dem Aufruf der Funktion sendto wird die Struktur mit den Informationen über den Absender wiederum zur Spezifikation der Zieladresse verwendet. Insgesamt hat der Server folgende Form:

> /* Server für UDP */
> #include <stdio.h>
> #include <winsock2.h>
> 
> #define SERVER_PORT 5432
> #define BUF_MAXLEN 256
> long WinsockStartup();
> 
> void main()
> {
> int rc; /* Variable fuer Rueckgabewerte */
> int al;
> 
> SOCKET sock;
> SOCKADDR_IN addr;
> SOCKADDR_IN sender;
> 
> char buffer[BUF_MAXLEN];
> 
> addr.sin_addr.s_addr = 0;
> addr.sin_family = AF_INET;
> addr.sin_port = htons(SERVER_PORT);
> 
> /* Nur unter Windows erforderlich */
> rc = WinsockStartup();
> 
> /* Socket anlegen und binden */
> sock = socket(AF_INET,SOCK_DGRAM,NULL);
> bind(sock, (SOCKADDR*)&addr, sizeof addr);
> 
> /* In Endlosschleife auf Pakete warten
> * */
> for( ;; ) {
> printf( "Warten auf naechstes Paket... \n" );
> al = sizeof sender;
> rc = recvfrom( sock, buffer, sizeof buffer, 0,
> (SOCKADDR*)&sender, &al);
> printf("%d Bytes von ", rc );
> printf("Host: %s ", inet_ntoa ( sender.sin_addr ) );
> printf("Port: %d ", ntohs( sender.sin_port ) );
> printf("<%s>\n", buffer );
> // Bestaetigung schicken
> sendto( sock, "OKAY", 5, 0, (SOCKADDR*)&sender, al);
> }
> }

Das Programm verwendet die beiden Funktionen inet_ntoa und ntohst. Die Funktion inet_ntoa wandelt die interne Darstellung der IP-Adresse in der Struktur in eine ASCII-Zeichenkette um. ntohs (Network to host short) ist das Gegenstück zu der bereits bekannten Funktion htons. Wie der Name sagt, konvertiert sie einen short-Wert aus dem standardisierten Netzwerkformat in die auf dem lokalen Rechner verwendete Zahlendarstellung.

Nach dem Starten wartet der Server auf Pakete. Wenn er ein Paket erhalten hat, zeigt er die Informationen über das Paket an und schickt ein Paket mit einer Bestätigung. Der folgende Code zeigt einen dazu passenden Client:

> /* Client für UDP */
> #include <stdio.h>
> #include <winsock2.h>
> 
> #define SERVER_PORT 5432
> #define BUF_MAXLEN 256
> 
> long WinsockStartup();
> 
> int main()
> {
> SOCKET sock;
> SOCKADDR_IN addr;
> long rc;
> 
> char buffer[BUF_MAXLEN];
> char hostAdress[] = "127.0.0.1";
> 
> rc = WinsockStartup();
> 
> // socket anlegen
> sock=socket(AF_INET,SOCK_DGRAM,NULL);
> 
> // zuerst alles auf 0 setzten
> memset(&addr,0,sizeof(SOCKADDR_IN));
> addr.sin_family = AF_INET; // Adress Familie
> addr.sin_port = htons(SERVER_PORT); // Port-Nummer
> addr.sin_addr.s_addr = inet_addr(hostAdress); // host
> 
> for( ;; ) {
> sendto( sock, "test", 5, 0, (SOCKADDR*)&addr, sizeof SOCKADDR);
> rc = recvfrom( sock, buffer, sizeof buffer, 0, 0, 0);
> if( rc > 0 ) printf("%s\n", buffer);
> else printf(".");
> Sleep( 500 );
> }
> return 0;
> }

Der Client schickt ein Paket an den Server und wartet anschließend auf eine Bestätigung. Eine besondere Situation entsteht, wenn der vorhergehende sendto-Aufruf fehlschlug, da der Empfänger nicht erreichbar war. Der Socket erhält dann die ICMP-Meldung Port Unreachable. In diesem Zustand wird die Funktion recv-from unmittelbar beendet und gibt einen entsprechenden Fehlerwert zurück. Das Programm erkennt durch den Vergleich des Rückgabewertes auf kleiner 0 solche Fehler und meldet dies durch die Ausgabe eines Punkts.

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Realisierung in Perl

Wie in der Einleitung erwähnt, ist die Socket-Schnittstelle nicht auf die Sprache C beschränkt, sondern steht auch in anderen Programmiersprachen zur Verfügung. Das Socket-API kapselt die Netzwerkkommunikation soweit, dass auch Anwendungen in verschiedenen Programmiersprachen über Sockets Informationen austauschen können. Als ein Beispiel sei ein UDP-Client für den oben beschriebenen Server in der Sprache Perl (Practical Extraction and Report Language) [2] angegeben. Der folgende Code zeigt einen Client, der Eingaben von der Tastatur annimmt, jede Zeile an den Server schickt, die jeweilige Antwort empfängt und am Bildschirm ausgibt.

> # UDP-Client
> use Socket;
> 
> ${port} = 5432;
> ${dest} = 'localhost';
> 
> # UDP-Socket anlegen
> socket( S, PF_INET, SOCK_DGRAM, 0) || die "socket: $!";
> 
> # Adresse des Servers
> ${hisiaddr} = inet_aton(${dest}) || die "unknown host";
> ${hispaddr} = sockaddr_in(${port}, ${hisiaddr});
> 
> # Eingaben einlesen, an Server schicken und Antwort ausgeben
> while( <STDIN> ) {
> send(S, ${_}, 0, ${hispaddr} ) || die "send: $!";
> ${ok} = '';
> recv(S, ${ok}, 10, 0) || die "recv: $!";
> print "${ok}\n";

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

Literatur

© www.tecCHANNEL.de

[1] W. R. Stevens. UNIX network programming. Prentice Hall, Englewood Cliffs, 1990

[2] L. Wall and R.L. Schwartz. Programmieren mit Perl. O’Reilly Verlag, 2001

Dn151202.590B5404BFEA7F06684DB47B00539355(de-de,TechNet.10).pngZum Seitenanfang

| Home | Technische Artikel | Community