Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 11 Netzwerkprogrammierung
  gp 11.1 Einführung
  gp 11.2 Aufbau von Netzwerken
    gp 11.2.1 ISO/OSI und TCP/IP – Referenzmodell
    gp 11.2.2 Das World Wide Web (Internet)
  gp 11.3 TCP/IP – Aufbau und Struktur
    gp 11.3.1 Netzwerkschicht (Datenübertragung)
    gp 11.3.2 Internetschicht
    gp 11.3.3 Transportschicht (TCP, UDP)
    gp 11.3.4 Anwendungsschicht
  gp 11.4 TCP Socket
  gp 11.5 Kommunikationsmodell
  gp 11.6 Grundlegende Funktionen zum Zugriff auf die Socket-Schnittstelle
    gp 11.6.1 Ein Socket anlegen – socket()
    gp 11.6.2 Verbindungsaufbau – connect()
    gp 11.6.3 Socket mit einer Adresse verknüpfen – bind()
    gp 11.6.4 Auf Verbindungen warten – listen() und accept()
    gp 11.6.5 Senden und Empfangen von Daten (1) – write() und read()
    gp 11.6.6 Senden und Empfangen von Daten (2) – send() und recv()
    gp 11.6.7 Verbindung schließen – close()
  gp 11.7 Aufbau eines Clientprogramms
    gp 11.7.1 Zusammenfassung: Clientanwendung und Quellcode
  gp 11.8 Aufbau des Serverprogramms
    gp 11.8.1 Zusammenfassung: Serveranwendung und Quellcode
  gp 11.9 IP-Adressen konvertieren, manipulieren und extrahieren
    gp 11.9.1 inet_aton(), inet_pton() und inet_addr()
    gp 11.9.2 inet_ntoa() und inet_ntop()
    gp 11.9.3 inet_network()
    gp 11.9.4 inet_netof()
    gp 11.9.5 inet_lnaof()
    gp 11.9.6 inet_makeaddr()
  gp 11.10 Namen und IP-Adressen umwandeln
    gp 11.10.1 Name-Server
    gp 11.10.2 Informationen zum Rechner im Netz – gethostbyname und gethostbyaddr
    gp 11.10.3 Service-Informationen – getservbyname() und getservbyport()
  gp 11.11 Der Puffer
  gp 11.12 Standard-E/A-Funktionen verwenden
    gp 11.12.1 Pufferung von Standard-E/A-Funktionen
  gp 11.13 Parallele Server
  gp 11.14 Syncrones Multiplexing – select()
  gp 11.15 POSIX-Threads und Netzwerkprogrammierung
  gp 11.16 Optionen für Sockets setzen bzw. erfragen
    gp 11.16.1 setsockopt()
    gp 11.16.2 getsockopt()
    gp 11.16.3 Socket-Optionen
  gp 11.17 UDP
    gp 11.17.1 Clientanwendung
    gp 11.17.2 Serveranwendung
    gp 11.17.3 recvfrom() und sendto()
    gp 11.17.4 bind() verwenden oder weglassen
  gp 11.18 UNIX-Domain-Sockets (IPC)
    gp 11.18.1 Die Adressstruktur von UNIX-Domain-Sockets
    gp 11.18.2 Lokale Sockets erzeugen – socketpair()
  gp 11.19 Multicast-Socket
    gp 11.19.1 Anwendungsgebiete von Multicast-Verbindungen
  gp 11.20 Nicht blockierende I/O-Sockets
  gp 11.21 Etwas zu Streams und TLI, Raw Socket, XTI
    gp 11.21.1 Raw Socket
    gp 11.21.2 TLI und XTI
    gp 11.21.3 RPC (Remote Procedure Call)
  gp 11.22 IPv4 und IPv6
    gp 11.22.1 IPv6 – ein wenig genauer
  gp 11.23 Netzwerksoftware nach IPv6 portieren
    gp 11.23.1 Konstanten
    gp 11.23.2 Strukturen
    gp 11.23.3 Funktionen
  gp 11.24 Sicherheit und Verschlüsselung


Rheinwerk Computing

11.6 Grundlegende Funktionen zum Zugriff auf die Socket-Schnittstelle  downtop

Um auf eine TCP-Verbindung bzw. auf die Sockets zugreifen zu können, stellt Ihnen das Betriebssystem mittels Systemaufrufen eine Menge Funktionen zur Verfügung. Im folgenden Abschnitt will ich Ihnen die grundlegenden Funktionen zum Verbindungsaufbau, zur Datenübertragung und zum Beenden einer solchen Verbindung vorstellen.


Rheinwerk Computing

11.6.1 Ein Socket anlegen – socket()  downtop

EinSocket können Sie mit der Funktion socket() anlegen:

#include <sys/types.h>
#include <sys/socket.h>
int socket (int family, int type, int protocol);

Mit dem ersten Parameter geben Sie die Protokollfamilie an, die Sie verwenden wollen. Gewöhnlich werden Sie meistens hierbei AF_INET für die Internet-Familie (TCP/IP) angeben oder – sofern Sie Sockets zur Interprozesskommunikation innerhalb eines UNIX-Rechners verwenden wollen – AF_UNIX.


Tabelle 11.1    Protokollfamilie für den ersten Parameter von socket()

Adressfamilie Protokollfamilie Bedeutung
AF_INIX, AF_UNIX, AF_LOCAL PF_UNIX, PF_LOCAL Lokale Kommunikation (IPC)
AF_INET PF_INET IPv4 Internet-Protokoll (TCP/IP)
AF_INET6 PF_INET6 IPv6 Internet-Protokoll
AF_IPX PF_IPX IPX – Novell-Protokoll
AF_NETLINK PF_NETLINK Kernel User Interface Device Netlink(7)
AF_X25 PF_X25 ITU-T X.25/ISO-8208-Protokoll x25(7)
AF_AX25 PF_AX25 Amateur Radio AX.25-Protokoll
AF_ATMPCV PF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK PF_APPLETALK AppleTalk DDS
AF_PACKET PF_PACKET Low-Level-Packet-Schnittstelle


Hinweis   Unter BSD 4.x werden für die Protokollfamilien die Konstanten PF_* verwendet, während die Konstanten AF_* für die Adressfamilien verwendet werden. Allerdings sollte Sie das nicht verwirren, denn in der BSD-Dokumentation ist zu lesen, dass die Protokollfamilie dasselbe ist wie die Adressfamilie.


Mit dem zweiten Parameter spezifizieren Sie den Typ des Sockets. Gewöhnlich gibt man hier entweder SOCK_STREAM (Streaming-Protokoll) an, womit TCP als Transportprotokoll verwendet wird, oder den Typ SOCK_DGRAM (Datagrammprotokoll) für UDP als Transportprotokoll. Natürlich können hierbei noch weitere Angaben gemacht werden – die allerdings in der Praxis recht selten benötigt werden. Hier für den Fall der Fälle ein Überblick über die verschiedenen möglichen Angaben zum Typ des Sockets, die nicht alle bei den Protokollfamilien implementiert sein müssen.


Tabelle 11.2    Typ des Sockets wird als zweiter Parameter von socket() verwendet.

Socket-Typ Bedeutung
SOCK_STREAM Damit wird festgelegt, dass über eine (zuverlässige) Zwei-Wege-Verbindung Datenströme übertragen werden (TCP).
SOCK_DGRAM Jedes Datenpaket (mit einer festen Länge) wird einzeln betrachtet (Datagramm-Transportprotokoll – UDP).
SOCK_SEQPACKET Damit haben Sie einen verlässlichen Übertragungspfad für Datagramme mit fester Länge, nur muss der Empfänger ein ganzes Paket mit jedem Funktionsaufruf lesen.
SOCK_RAW Damit haben Sie Zugriff auf interne Netzwerkprotokolle und Schnittstellen (nur IP).
SOCK_RDM Bietet eine Datagrammschicht, die das Sortieren der Pakete nicht garantiert.
SOCK_PACKET veraltet, stattdessen PF_PACKET benutzen (siehe man packet(7))

Mit dem dritten Parameter von socket() können Sie das zu benutzende Protokoll auswählen. Geben Sie hierfür 0 an, was meistens in der Praxis auch der Fall ist, wird das Standardprotokoll für den jeweiligen Socket-Typ ausgewählt, z. B. wird bei SOCK_STREAM das TCP-Protokoll und bei SOCK_DGRAM das UDP-Protokoll verwendet. Da es allerdings ohnehin zu jedem Socket einer Protokollfamilie nur ein einziges Protokoll gibt, ist klar, warum gewöhnlich 0 angegeben wird.

Bei Erfolg gibt die Funktion socket() den Socket-Deskriptor zurück. Ansonsten wird bei einem Fehler -1 zurückgegeben. Mit diesem Socket-Deskriptor (eine positive Ganzzahl) können Sie das Socket von nun an verwenden.

Natürlich haben Sie mit dem Anlegen eines Sockets noch lange keine Verbindung erstellt. Sie haben lediglich festgelegt, mit welchem Protokoll das Socket arbeitet. Sie können sich das gerne wie das Installieren einer Stromsteckdose vorstellen. In aller Welt gibt es viele verschiedene Formen und Normen.

Socket-Deskriptor

Bei der Syntax von socket() war die Rede von einem Socket-Deskriptor, den diese Funktion zurückgibt. Die erste Frage, die sich wohl stellt, dürfte sein, inwiefern sich dieser Socket-Deskriptor von einem Filedeskriptor unterscheidet? Und in der Tat ist der Unterschied auf den ersten Blick nicht groß. Sobald Sie einen Socket erzeugt haben, können Sie mithilfe des int-Wertes z. B. mit den elementaren Funktionen wie u. a. read(), write() usw. darauf zurückgreifen. Dennoch gibt es ein paar geringfügige Dinge, die man zwischen einem Socket- und Filedeskriptor unterscheidet:

gp  Im Gegensatz zum Filedeskriptor kann aus einem Socket-Deskriptor nicht jederzeit gelesen bzw. geschrieben werden. Die Ein- bzw. Ausgabe ist bei den Socket-Deskriptoren nur möglich, wenn sich das Socket im richtigen Zustand befindet.
gp  Sie können kein lseek auf einen Socket-Deskriptor ausführen lassen.
gp  Die Einstellungen bzw. Attribute, die man mit ioctl() setzen bzw. abfragen kann, von Sokket-deskriptoren (im Gegensatz zu Filedeskriptoren) sind anders.
gp  Sockets kann man eine Adresse zuweisen, was bei Dateien und Pipes nicht geht.

Rheinwerk Computing

11.6.2 Verbindungsaufbau – connect()  downtop

Wollen Sie jetzt mit einem Prozess eine Verbindung zu einem anderen Prozess aufbauen und haben bereits ein Socket erzeugt, kann die Funktion connect() aufgerufen werden.

#include <sys/types.h>
#include <sys/socket.h>
int connect ( int sockfd,
              struct sockaddr *adresse, 
              socklen_t addressen_laenge );

Gewöhnlich wird der connect()-Aufruf vom Clientprogramm ausgeführt, um eine Verbindung zum Server herzustellen. Der erste Parameter gibt das Socket an, über das Sie auf die Verbindung zugreifen wollen. Der zweite Parameter adresse enthält die Anfangsadresse der Struktur sockaddr, worin sich die IP-Adresse des Rechners und die Portnummer befinden. Diese Struktur ist wie folgt definiert:

#include <sys/socket.h>
#include <netinet/in.h>
struct sockaddr_in {
    unsigned short int  sin_family;  /* AF_INET     */
    unsigned short int  sin_port;    /* Portnummer */
    struct in_addr      sin_addr;    /* IP-Adresse  */
};

Mit dem letzten Parameter in connect() geben Sie den Speicherbedarf des zweiten Parameters an. Bei erfolgreicher Verbindung gibt diese Funktion 0, ansonsten bei einem Fehler -1 zurück.


Hinweis   Wenn Sie UDP anstatt TCP verwenden, können Sie auf einen Aufruf von connect() verzichten. Dann allerdings müssen Sie die entsprechende Adressinformation bei den Funktionen sendto() zum Senden und recvfrom() zum Empfangen von Daten ergänzen.


Die Socket-Adressstruktur etwas genauer

Da die meisten Funktionen mit Sockets die Adresse einer Socket-Adressstruktur als Argument erwarten, soll hierauf etwas näher eingegangen werden, da diese Struktur schon häufiger zu Missverständnissen geführt hat. Häufig finden Sie die Struktur sockaddr_in in der Headerdatei <netinet/in.h> wie folgt definiert:

struct sockaddr_in {
   uint8_t        sin_len;     // Länge der Struktur
   sa_family_t    sin_family;  // AF_INET
   in_port_t      sin_port;    // 16-Bit-Portnummer
   struct in_addr sin_addr;    // 32-Bit-IPv4-Adresse 
   char           sin_zero[];  // nicht verwendet 
}
struct in_addr {
   in_addr_t  s_addr;          // 32-Bit-IPv4-Adresse
}

Im Gegensatz zur zuvor gezeigten Struktur finden Sie hierbei außerdem noch sin_len und sin_zero, die allerdings beide nicht dem POSIX-Standard entsprechen und daher ggf. vermieden werden sollten. Die verschiedenen Datentypen finden Sie in <sys/types.h> und <bits/types.h> definiert.

In der Praxis sollte man zunächst diese Struktur immer mit Nullen füllen, was gewöhnlich mit memset() oder bzero() gemacht wird.

struct sockaddr_in addr;
...
/* Vor der Verwendung mit Nullen füllen */
memset(&addr, 0, sizeof(addr));
...

An dieser Stelle folgt nun ein recht häufiger Stolperstein, über den viele Anfänger fallen. Denn beim genaueren Betrachten der Syntax aller Socket-Funktionen fällt immer auf, dass hierbei die Rede von einer Struktur namens sockaddr und nicht sockaddr_in ist, z. B.:

int connect(int s, struct sockaddr *serv_addr, int addrlen);

Die Syntax dieser Struktur sieht wie folgt aus:

struct sockaddr {
   uint8_t      sa_len;  // Länge der Struktur - nicht POSIX 
   sa_family_t  sa_family;   // Adressfamilie AF....
   char         sa_data[14]; // Protokollspezifische Adresse
}

Diese Struktur wurde so implementiert, dass diese unabhängig von der verwendeten Adressfamilie ist, was allerdings auch bedeutet, dass diese Struktur recht umständlich auszufüllen ist. Daher gibt es für IP-Anwendungen die Struktur sockaddr_in, womit die IP-Adresse und Portnummer getrennt eingetragen werden. Zum Glück sind diese beiden Strukturen im Speicher kompatibel, so dass eine einfache Typenumwandlung ausreicht, wenn Sie keine Warnung vom Compiler erhalten möchten.

struct sockaddr_in addr;
...
connect( fd, (struct sockaddr *)&addr, sizeof(addr));

Ähnlich sieht übrigens auch die Syntax zur Socket-Adressstruktur sockaddr_in6 für IPv6 aus, die ebenfalls in der Headerdatei <netinet/in.h> definiert ist:

struct sockaddr_in6 {
   uint8_t         sin6_len;         // Länge der Struktur
   sa_family_t     sin6_family;      // AF_INET6
   in_port_t       sin6_port;        // 16-Bit-Portnummer
   struct in6_addr sin6_addr;        // 128-Bit-IPv6-Adresse
   // Priorität in der Netzwerk-Byteanordnung
   uint32_t        sin6_flowinfo;    
}
struct in6_addr {
   uint8_t  s6_addr[16];             // 128-Bit-IPv6-Adresse 
}              // unter Linux findet man hier eine union ...

Sofern Sie also wieder etwas mit den Socket-Adressstrukturen durcheinander bringen sollten, so beziehen Sie sich am besten auf dieses Kapitel.


Rheinwerk Computing

11.6.3 Socket mit einer Adresse verknüpfen – bind()  downtop

Um dem zuvor erzeugten Socket mitzuteilen, zu welchem Port dieses gehört, wird die Funktion bind() verwendet.

#include <sys/types.h>
#include <sys/socket.h>
int bind ( int sockfd, 
           struct sockaddr *adresse,
           socklen_t adressen_laenge );

Erst durch den bind()-Aufruf nämlich macht der connect()-Aufruf Sinn. Denn damit wird »unser« Socket an einem Port befestigt, dessen Nummer beim connect()-Aufruf angegeben wird. Die Funktion von bind() lässt sich recht einfach erklären. Wenn Sie einen Bekannten anrufen wollen, haben aber aus Versehen die falsche Nummer eingegeben, kann es passieren, dass Sie entweder keinen Anschluss unter dieser Nummer bekommen oder aber den falschen Gesprächspartner. Der erste und der dritte Parameter hat somit dieselbe Bedeutung wie bei der Funktion connect(), und der zweite Parameter enthält die IP-Adresse und Portnummer, zu denen das Socket von nun an gehört. Bei Erfolg gibt auch hier die Funktion bind() 0, ansonsten bei einem Fehler -1 zurück.

bind() ist für Clients nicht unbedingt notwendig, da dann automatisch eine Ausgangs-IP-Adresse und ein Ausgangsport genommen wird. Ausgangs-IP, da je nach Zieladresse per connect() (z. B. 134.76.13.21 oder 192.168.201.1) auf 134.76 oder 192.168:1025 (auch noch beliebiger freier Port) gebunden wird. Bei Serveranwendungen hingegen ist bind() nötig, sonst ist das Socket ja nicht zu erreichen.


Rheinwerk Computing

11.6.4 Auf Verbindungen warten – listen() und accept()  downtop

Diese Funktion ist nur serverseitig sinnvoll. Mit der Funktion listen() setzen Sie das Socket in einen passiven Modus und teilen dem System mit, dass der (Server-)Prozess bereit ist, über dieses Socket eine Verbindung einzugehen. Damit wartet der Prozess quasi auf eine Verbindung (er lauscht am Socket). Natürlich ist es auch möglich, über einen Socket mehrere eintreffende Verbindungswünsche gleichzeitig entgegenzunehmen. Dabei wird eine Warteschlange eingerichtet, wo die neuen Verbindungswünsche hinten angefügt werden.

#include <sys/types.h>
#include <sys/socket.h>
int listen ( int sockfd, int warteschlange );

Mit dem ersten Parameter geben Sie das Socket an, das »belauscht« werden soll. Als zweiten Parameter geben Sie die maximale Anzahl von Verbindungen an, die gleichzeitig entgegengenommen werden können. Wobei natürlich niemals mehr als eine Anfrage (Request) gleichzeitig abgearbeitet werden kann. Jede weitere Verbindung (ein connect() vom Client) wird also vorerst noch auf einen Stapel (Warteschlange) gelegt, dessen Größe über listen() festgesetzt wird. Die Clientanwendung wird meistens dazu genötigt, so lange zu warten, bis der Server wieder frei ist. Dieser Vorgang wird auch als cachen bezeichnet. Die Anzahl der Verbindungen, die gleichzeitig gecachet werden können, geben Sie also mit dem zweiten Parameter von listen() an. Bei Erfolg gibt listen() 0, ansonsten bei einem Fehler -1 zurück.

Mit der Funktion accept() können Sie schließlich die noch wartenden oder eintreffenden Verbindungsaufbauwünsche bearbeiten.

#include <sys/types.h>
#include <sys/socket.h>
int accept ( int sockfd,
             struct sockaddr *adresse, 
             socklen_t *adressen_laenge );

Durch einen erfolgreichen accept()-Aufruf wird ein neuer Socket-Deskriptor erzeugt, über den der aufrufende Prozess die Datenübertragung durchführen kann. Der neue Socket-Deskriptor, der von der Funktion accept() zurückgegeben wird, erbt alle Eigenschaften des Sockets, das zuvor mit listen() »belauscht« wurde. Ein erfolgreicher Aufruf von accept() führt auf der Gegenseite auch zu einem erfolgreichen Aufruf von connect() – was somit eine erfolgreiche TCP-Verbindung darstellen würde.

Als ersten Parameter erhält accept() das Socket, das beim Erzeugen mittels socket() erstellt wurde. Der zweite Parameter enthält die Adresse des Partnerprozesses, und der dritte Parameter enthält die Länge (Speicherbedarf) des zweiten Parameters. Bei einem Fehler gibt accept()-1 und bei Erfolg, wie schon erwähnt, den Socket-Deskriptor des akzeptierten Sockets zurück.


Rheinwerk Computing

11.6.5 Senden und Empfangen von Daten (1) – write() und read()  downtop

Zum Senden und Empfangen von Daten können Sie auf die ganz gewöhnlichen Standardfunktionen zurückgreifen. Mit write() können Sie Daten an ein Socket verschicken, und mit read() können Sie die Daten wieder aus dem Socket lesen. Da die Funktionen read() und write() bereits ausführlich behandelt wurden, kann ich mir hier eine weitere Erklärung ersparen. Hier nochmals die Syntax der beiden Funktionen:

#include <unistd. h.>
ssize_t write( int socketfd, const void *data,
               size_t data_len);
ssize_t read( int socketfd, void *data, size_t data_len );

Bitte beachten Sie außerdem, dass sich read() und write() bei TCP-Sockets anders verhalten. Bei normalen Daten können Sie (fast) beliebig angeben, wie viele Daten Sie auf einmal lesen oder schreiben wollen. Bei der Netzwerkprogrammierung sind diesbezüglich (Puffer-)Begrenzungen gesetzt. Ist diese bestimmte Puffergrenze erreicht, bricht der Lese- bzw. Schreibvorgang ab und muss erneut ausgeführt werden, um die noch vorhandenen Bytes zu lesen oder zu schreiben (das ist auch normalerweise bei read() der Fall, wenn z. B. ein Gerätetreiber in /dev weniger zurückgibt als data_len). Sie müssen praktisch den Rückgabewert von read() überprüfen, ob tatsächlich schon alle n Bytes (dritter Parameter) gelesen wurden – sonst kann (wird) es passieren, dass nicht alles gelesen wird. Mit write() verläuft dies recht ähnlich.


Rheinwerk Computing

11.6.6 Senden und Empfangen von Daten (2) – send() und recv()  downtop

Sofern Sie nicht unter Linux oder einem UNIX arbeiten, können Sie auch die Funktionsaufrufe send() zum Versenden von Daten und recv() zum Einlesen von Daten anstatt write() und read() verwenden. Der Hauptvorteil der Funktion send() und recv() liegt in der Portierung auf andere Systeme (z. B. MS Windows), wo die Funktionen read() und write() ausschließlich nur auf Dateien anwendbar sind. Die Funktionen send() und recv() unterscheiden sich von read() und write(), abgesehen von der Syntax, lediglich durch eine Angabe eines weiteren vierten Parameters. Zuerst die Syntax zu send():

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send ( int socketfd, const void *data,
               size_t data_len, unsigned int flags );

Mit dem ersten Parameter geben Sie das Socket an, mit dem die Daten gesendet werden sollen. Im zweiten Parameter befinden sich die Daten, die gesendet werden sollen, und im dritten die Länge dieser Daten. Im letzten Parameter können noch Flags gesetzt werden. Gewöhnlich wird hierfür jedoch 0 angegeben. Über genauere Angaben zu den Flags lesen Sie bitte die entsprechende Manual Page. Die Funktion send() gibt die Anzahl der gesendeten Zeichen zurück oder bei einem Fehler -1.

Das Gegenstück zur Funktion send() ist die Funktion recv():

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv ( int socketfd, void *data ,
               size_t data_len, unsigned int flags );

Als erster Parameter wird der Socket erwartet, aus dem die Daten empfangen werden sollen. Die Daten selbst werden in den Speicher bei data gelesen. Die Länge für data müssen Sie mit data_len angeben. Mit data_len geben Sie die Anzahl der zu lesenden Bytes an. Dabei ist die Angabe von data_len enorm wichtig, denn eine Angabe, die mehr Speicher beinhaltet, als für data zur Verfügung steht, kann unweigerlich mit einem Buffer Overflow ausgenutzt werden kann. Auch hier wird für das Flag gewöhnlich 0 eingegeben. Mehr Informationen zu den Flags erhalten Sie auch hier unter man 2 recv. Die Funktion recv() gibt die Anzahl der empfangenen Bytes zurück oder bei einem Fehler -1.

Im Falle von send() und recv() gilt dieselbe Aussage wie schon bei write() und read(). Sie können sich auch hier nicht darauf verlassen, dass send() alle Daten auf einmal losschickt, sondern müssen auch hier durch Überprüfen des Rückgabewertes abklären, ob ein erneuter Funktionsaufruf nötig ist.


Rheinwerk Computing

11.6.7 Verbindung schließen – close()  toptop

Die Funktion close() können Sie dazu verwenden, um eine Verbindung des Sockets, unter der Angabe des Socket-Deskriptors, wieder zu schließen. Diese Funktion kennen Sie auch bereits aus Kapitel 2, weshalb hierzu nicht mehr viel zu sagen ist.

#include <unistd. h.>
int close ( int sockfd );
 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern