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

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 28 Netzwerkprogrammierung und Cross-Plattform-Entwicklung
  gp 28.1 Begriffe zur Netzwerktechnik
    gp 28.1.1 IP-Nummern
    gp 28.1.2 Portnummer
    gp 28.1.3 Host- und Domainname
    gp 28.1.4 Nameserver
    gp 28.1.5 Das IP-Protokoll
    gp 28.1.6 TCP und UDP
    gp 28.1.7 Was sind Sockets?
  gp 28.2 Headerdateien zur Socketprogrammierung
    gp 28.2.1 Linux/UNIX
    gp 28.2.2 Windows
  gp 28.3 Client-/Server-Prinzip
    gp 28.3.1 Loopback-Interface
  gp 28.4 Erstellen einer Client-Anwendung
    gp 28.4.1 socket() – Erzeugen eines Kommunikationsendpunktes
    gp 28.4.2 connect() – Client stellt Verbindung zum Server her
    gp 28.4.3 Senden und Empfangen von Daten
    gp 28.4.4 close(), closesocket()
  gp 28.5 Erstellen einer Server-Anwendung
    gp 28.5.1 bind() – Festlegen einer Adresse aus dem Namensraum
    gp 28.5.2 listen() – Warteschlange für eingehende Verbindungen einrichten
    gp 28.5.3 accept() und die Serverhauptschleife
  gp 28.6 (Cross-Plattform)TCP-Echo-Server
    gp 28.6.1 Der Client
    gp 28.6.2 Der Server
  gp 28.7 Cross-Plattform-Development
    gp 28.7.1 Abstraktion Layer
    gp 28.7.2 Headerdatei Linux/UNIX
    gp 28.7.3 Linux/UNIX-Quelldatei
    gp 28.7.4 Headerdatei MS-Windows
    gp 28.7.5 Windows-Quelldatei
    gp 28.7.6 All together – die main-Funktionen
    gp 28.7.7 Ein UDP-Beispiel
    gp 28.7.8 Mehrere Clients gleichzeitig behandeln
  gp 28.8 Weitere Anmerkungen zur Netzwerkprogrammierung
    gp 28.8.1 Das Datenformat
    gp 28.8.2 Der Puffer
    gp 28.8.3 Portabilität
    gp 28.8.4 Von IPv4 nach IPv6
    gp 28.8.5 RFC-Dokumente (Request for Comments)
    gp 28.8.6 Sicherheit


Galileo Computing - Zum Seitenanfang

28.4 Erstellen einer Client-Anwendung  downtop

In diesem Abschnitt soll es jetzt darum gehen, was für Funktionen grundlegend verwendet werden, um eine Client-Anwendung zu erstellen.


Galileo Computing - Zum Seitenanfang

28.4.1 socket() – Erzeugen eines Kommunikationsendpunktes  downtop

Der erste Schritt einer Kommunikationsverbindung – egal ob jetzt Server oder Client – ist immer erst mal, einen Socket vom Betriebssystem anzufordern. Dabei ist noch egal, wer mit wem kommunizieren will. Das Erzeugen eines Sockets (Kommunikationsendpunkt) können Sie sich wie das Installieren einer Stromsteckdose vorstellen. Ähnlich wie bei den Stromsteckdosen weltweit, wo es ja auch unterschiedliche Formen und Spannungen gibt, muss auch beim Anlegen eines Sockets angegeben werden, was hier alles »eingesteckt« werden kann. Hierzu erstmal die Syntax der Funktion socket() für Linux/UNIX:

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

Und hier die Syntax für MS-Windows:

#include <winsock.h>
SOCKET socket(int af, int type, int protocol);

Auf beiden Systemen haben diese Funktionen eine fast identische Syntax – abgesehen vom Rückgabewert, der unter MS-Windows SOCKET lautet. Allerdings ist SOCKET letztendlich nichts anderes als eine Typendefinition von int und somit könnten Sie in der Praxis hierfür auch int verwenden. Als Rückgabewert erhalten Sie bei beiden Versionen den Socket-Deskriptor.

Bei einem Fehler gibt die Linux/UNIX-Version –1 zurück. Den Fehler können Sie mit dem Fehlercode von errno auswerten (bspw. mit perror() oder strerror()).

Unter MS-Windows wird bei einem Fehler die Konstante SOCKET_ERROR (ebenfalls mit –1 definiert) zurückgegeben. Hierbei können Sie den Fehlercode mit der Funktion WSAGetLastError() ermitteln.

Mit dem ersten Parameter domain bzw. af geben Sie die Adressfamilie (= Protokollfamilie) an, die Sie verwenden wollen. Eine komplette Liste aller auf Ihrem System unterstützten Protokolle finden Sie dabei in der Headerdatei <sys/socket.h>. Dennoch hierzu ein Überblick zu den gängigeren und häufiger verwendeten Protokollen.


Tabelle 28.1   Einige gängige Adressfamilien

Adressfamilie Bedeutung
AF_UNIX UNIX Domain Sockets; wird gewöhnlich für lokale Interprozesskommunikation verwendet.
AF_INET Internet IP-Protokoll Version 4 (IPv4)
AF_INET6 Internet IP-Protokoll Version 6 (IPv6)
AF_IRDA IRDA-Sockets; bspw. via Infarot
AF_BLUETOOTH Bluetooth-Sockets

Mit dem zweiten Parameter der Funktion socket(), geben Sie den Socket-Typ an. Damit legen Sie die Übertragungsart der Daten fest. Für Sie sind hierbei erst mal nur die symbolischen Konstanten SOCK_STREAM für TCP und SOCK_DGRAM für UDP interessant.

Mit dem dritten Parameter können Sie ein Protokoll angeben, dass Sie zur Übertragung verwenden wollen. Verwenden Sie hierfür 0, was meistens der Fall ist, wird das Standardprotokoll verwendet, welches dem gewählten Socket-Typen (zweiter Parameter) entspricht. Im Fall von SOCK_STREAM wird TCP und bei SOCK_DGRAM wird UDP verwendet. Weitere mögliche Werte, ohne jetzt genauer darauf einzugehen, wären hierbei IPPROTO_TCP (TCP-Protokoll), IPPROTO_UDP (UDP-Protokoll), IPPROTO_ICMP (ICMP-Protokoll) und IPPROTO_RAW (wird bei Raw-Sockets verwendet). Wenn Sie allerdings bspw. für den Socket-Typen SOCK_STREAM angegeben haben und wollen das TCP-Protokoll verwenden, müssen Sie nicht extra noch beim dritten Parameter IPPROTO_TCP angeben. Mit der Angabe von 0 wird dieses Protokoll standardmäßig verwendet.

Somit sieht das Anfordern eines Sockets folgendermaßen aus:

    // Erzeuge das Socket – Verbindung über TCP/IP
    sock = socket( AF_INET, SOCK_STREAM, 0 );
    if (sock < 0) {
        // Fehler beim Erzeugen des Sockets
    }

Hinweis   Damit hier keine Missverständnisse entstehen, dass Erzeugen eines Sockets muss auch auf der Serverseite durchgeführt werden. Womit sonst, als über Sockets, will sich ein Client mit dem Server unterhalten.



Galileo Computing - Zum Seitenanfang

28.4.2 connect() – Client stellt Verbindung zum Server her  downtop

Nachdem mit den Sockets die Kommunikationsendpunkte erzeugt wurden, kann der Client nun versuchen, eine Verbindung zum Server-Socket herzustellen. Dies wird mit der Funktion connect() versucht – welche unter Linux/UNIX folgende Syntax hat:

#include <sys/types.h>
#include <sys/socket.h>
int connect (
   int socket,
   const struct sockaddr *addr,
   int addrlen
);

Und die Syntax unter MS-Windows:

#include <winsock.h>
int connect (
   SOCKET s,
   const struct sockaddr FAR* addr,
   int addrlen
);

Auch hier unterscheidet sich die Syntax nicht erheblich voneinander und auch die Bedeutungen der einzelnen Parameter sind wieder dieselben. Bei einer erfolgreichen Ausführung geben beide Funktionen 0, ansonsten bei einem Fehler –1 (gleichwertig unter MS-Windows mit SOCKET_ERROR) zurück. Den Fehler können Sie auch hier wieder mit der Fehlervariablen errno (unter Linux/UNIX) oder mit der Funktion WSAGetLastError() (unter MS-Windows) ermitteln.

Als erster Parameter wird der Socket-Deskriptor erwartet, über den Sie die Verbindung herstellen wollen. Dies ist der Rückgabewert, den Sie von der Funktion socket() erhalten haben.

Um eine Verbindung zu einem anderen Rechner aufzubauen, werden logischerweise auch Adressinformationen benötigt, womit sich der Client verbinden will. Die Adressinformationen über den gewünschten Verbindungspartner tragen Sie im zweiten Parameter der Funktion connect() ein. Um sich mit dem Server zu verbinden, benötigen Sie Informationen über die Adressfamilie (Protokollfamilie), die Portnummer und logischerweise die IP-Adresse. Eingetragen werden diese Informationen mit dem zweiten Parameter der Struktur sockaddr, welche folgendermaßen definiert ist:

struct sockaddr {
   sa_family_t sa_family; // Adressfamilie AF_XXX
   char sa_data[14];      // Protokolladresse(IP-Nr. und Portnr.)
 };

Da diese Struktur allerdings recht umständlich auszufüllen ist, wurde für IP-Anwendungen eine spezielle Struktur hierfür eingeführt, womit es möglich ist, die IP-Nummer und die Portnummer getrennt einzutragen.

struct sockaddr_in {
   sa_family sin_family;          // Adressfamilie AF_XXX
   unsigned short int sin_port;   // Portnummer
   struct in_addr sin_addr;       // IP-Adresse
   unsigned char pad[8];          // Auffüllbytes für sockaddr
 };

Da beide Strukturen im Speicher gleichwertig sind, reicht es aus, eine einfache Typenumwandlung bei connect() vorzunehmen. Mit dem letzten Parameter (addrlen) von connect() geben Sie die Länge in Bytes von sockaddr mit dem sizeof-Operator an.

Ausfüllen von sockaddr_in

In der Strukturvariablen sin_family geben Sie die Adressfamilie (Protokollfamilie) an mit der Sie kommunizieren wollen. Gewöhnlich gibt man hierfür dieselbe Familie an, wie schon beim ersten Parameter der Funktion socket() .

In sin_port geben Sie die Portnummer an, über die Sie mit dem Server in Kontakt treten wollen. Wichtig ist hierbei, dass Sie den Wert im Network Byte Order angeben. Es genügt also nicht, wenn Sie sich bspw. mit einem Webserver verbinden wollen, als Portnummer hier einfach 80 hinzuschreiben. Es muss hierbei auch auf die verschiedenen Architekturen, die es in heterogenen Netzwerken gibt, Rücksicht genommen werden. Denn auf den verschiedenen Architekturen gibt es unterschiedliche Anordnungen der Bytes zum Speichern von Zahlen. So wird bei der Anordnung gewöhnlich zwischen Big-Endian und Little-Endian unterschieden. Man spricht dabei gerne vom Zahlendreher. Beim Big-Endian wird das höchstwertige Byte an der niedrigsten Adresse gespeichert, das Zweithöchste an der nächsten Adresse, und so weiter. Bei der Anordnung von Little-Endian ist dies genau umgekehrt. Dabei wird das niedrigstwertige Byte an der niedrigsten Stelle gespeichert, das Zweitniedrigste an der nächsten Stelle usw.

Da man sich mit Big-Endian (auch als Network Byte Order bezeichnet) auf eine einheitliche Datenübertragung geeinigt hat, brauchen Sie sich keine Gedanken um verschiedene Architekturen zu machen.

Um jetzt aus einer lokal verwendeten Byte-Reihenfolge (Host Byte Order) eine Network Byte Order-Reihenfolge oder umgekehrt zu konvertieren, stehen Ihnen die folgenden vier Funktionen zur Verfügung:

#include <netinet/in.h>
// Rückgabe : network-byte-order
// Parameter: host-byte-order
unsigned short int htons(unsigned short int hostshort);
// Rückgabe : network-byte-order
// Parameter: host-byte-order
unsigned long int htonl(unsigned long int hostlong);
// Rückgabe  :  host-byte-order
// Parameter : network-byte-order
unsigned short int ntohs(unsigned short int netshort);
// Rücktgabe :  host-byte-order
// Parameter : network-byte-order
unsigned long int ntohl(unsigned long int netlong);

Nicht jeder kennt allerdings die entsprechenden Portnummern zum entsprechenden Dienst. Hierbei kann die Funktion getservbyname() helfen. Dieser Funktion übergeben Sie den Namen eines Dienstes und das Transportprotokoll als Parameter. Anschließend sucht getservbyname() in einer speziellen Datei nach einem Eintrag, der dazu passt und gibt die Portnummer zurück. Hierfür gibt es eine spezielle Struktur in der Headerdatei netdb.h, womit Sie an die Informationen zu den entsprechenden Diensten kommen.

struct servent {
   char *s_name;      // Offizieller Name vom Service
   char **s_aliases;  // Alias Liste
   int  s_port;       // Portnummer zum Servicenamen
   char *s_proto;     // verwendetes Protokoll
};

Eine kurze Beschreibung der einzelnen Strukturvariablen:

gp  s_name – Offizieller Servicename
gp  s_aliases – Ein Stringarray mit eventuellen Aliasnamen zum Service falls vorhanden. Das letzte Element in der Liste ist NULL.
gp  s_port – Die Portnummer zum Servicename
gp  s_proto – Der Name des zu verwendenden Protokolls

Hier die Syntax zu getservbyname():

#include <netdb.h>
struct servent *getservbyname ( const char *name,
                                const char *proto );

Bei Angaben des Dienstes name und dem Protokoll proto, liefert Ihnen diese Funktion bei Erfolg eine Adresse auf die Information in struct servent. Bei einem Fehler wird NULL zurückgegeben.

Die IP-Adresse geben Sie in der Strukturvariablen sin_addr an. Allerdings wird auch hier die Network Byte Order-Reihenfolge erwartet. Hierbei ist uns allerdings die Funktion inet_addr() (oder die etwas sicherere Alternative inet_aton()) behilflich. Hierbei können Sie die IP-Adresse als String angeben und bekommen einen für sin_addr benötigten 32-Bit Wert im Network Byte Order zurück.

Wenn der Client den Dienst eines Servers verwenden will, muss jenem natürlich dessen IP-Adresse bekannt sein. Meistens gibt ein Endanwender aber als Adresse den Rechnernamen anstatt der IP-Adresse an, da dieser einfacher zu merken ist. Damit also ein Client aus dem Rechnernamen (bspw. www.google.de) eine IP-Adresse (216.239.59.99) erhält, wird die Funktion gethostbyname() verwendet.

#include <netdb.h>
struct hostent *gethostbyname(const char *rechnername);

Um also aus einem Rechnername eine IP-Adresse und weitere Informationen zu ermitteln, steht ein so genannter Nameserver zur Verfügung – dieser Rechner ist für die Umsetzung zwischen Rechnernamen und IP-Nummern zuständig. Selbst auf Ihrem Rechner finden Sie solche Einträge der lokalen IP-Nummern in der Datei /etc/hosts hinterlegt. Im Internet hingegen werden diese Daten in einer eigenen Datenbank gehalten. Um solche Informationen zu den einzelnen Rechnern zu erhalten, ist in der Headerdatei netdb.h folgende Struktur definiert:

struct hostent {
   char *  h_name;
   char ** h_aliases;
   short   h_addrtype;
   short   h_length;
   char ** h_addr_list;
 };

Eine kurze Beschreibung der einzelnen Strukturvariablen:

gp  h_name – Offizieller Name des Rechners.
gp  h_aliases – Ein Stringarray, worin sich eventuelle Aliasnamen befinden. Das letzte Element ist immer NULL.
gp  h_addrtyp – Hier steht der Adresstyp, was gewöhnlich AF_INET für IPv4 ist.
gp  h_length – Hier befindet sich die Länge der nummerischen Adresse.
gp  h_addr_list – Hierbei handelt es sich um ein Array von Zeigern auf die Adressen für den entsprechenden Rechner.

Die Funktion gethostbyname() gibt bei Erfolg einen Zeiger auf struct hostent des gefundenen Rechners zurück, ansonsten bei einem Fehler NULL. Die letzte Strukturvariable pad in der Struktur sockaddr_in, wird lediglich als Lückenfüller verwendet, um sockaddr_in auf die Größe von sockaddr aufzufüllen.

Wenn Sie jetzt alle Strukturvariablen der Struktur sockaddr_in mit Werten belegt haben, können Sie die Funktion connect() aufrufen und bei stehender (erfolgreicher) Verbindung Daten austauschen (Senden und Empfangen).

Hier nochmals ein Codeausschnitt, wie ein »Auffüllen« der Struktur sockaddr_in und der anschließende Aufruf der Funktion connect() von statten geht. Im Beispiel wird versucht, mit einem Webserver (Port 80; HTTP), dessen IP-Adresse Sie als Argumente in der Kommandozeile übergeben haben, zu verbinden.

struct sockaddr_in server;
unsigned long addr;
...
// Alternative zu memset() -> bzero()
memset( &server, 0, sizeof (server));
addr = inet_addr( argv[1] );
memcpy( (char *)&server.sin_addr, &addr, sizeof(addr));
server.sin_family = AF_INET;
server.sin_port = htons(80);
...
// Baue die Verbindung zum Server auf
if (connect(sock,(struct sockaddr*)&server, sizeof(server)) < 0){
        // Fehler beim Verbindungsaufbau ...
    }

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.



Galileo Computing - Zum Seitenanfang

28.4.3 Senden und Empfangen von Daten  downtop

Nachdem Sie sich erfolgreich mit dem Server verbunden haben, können Sie anfangen, Daten an dem Server zu senden bzw. welche zu empfangen. Hierzu gibt es jeweils für TCP und UDP ein Funktionspaar. Es war hierbei ja schon mal die Rede, dass man mit Sockets ähnlich wie bei Dateien mit Filedeskriptoren arbeiten kann. Und in der Tat, unter Linux/UNIX kann der Austausch von Daten über Sockets auch mit den Systemcalls read() und write() stattfinden. Allerdings ist dies unter MS-Windows erst ab den Versionen NT/2000/XP mit den Funktionen ReadFile() und WriteFile() möglich.


Hinweis   Natürlich gilt auch hier, die Funktionen zum Senden und Empfangen gelten nicht nur für die Clients, sondern auch für die Serveranwendung.


send() und recv() – TCP

Zum Senden von Daten von einem Socket für Stream-Sockets (TCP-Sockets) wird gewöhnlich die Funktion send() verwendet, welche unter Linux/UNIX folgende Syntax besitzt:

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

Und unter MS-Windows mit Winsock sieht die Syntax wieder ähnlich aus:

#include <winsock.h>
int send ( SOCKET s, const char FAR* data,
           int data_len, int flags );

Vergleicht man diese Funktion mit write() können Sie hierbei ähnliche Parallelen ziehen. Mit dem ersten Parameter geben Sie den Socket-Deskriptor an, über dem Sie die Daten senden wollen. Im zweiten Parameter wird ein Zeiger auf den Speicherbereich erwartet, indem sich die Daten befinden. Die Größe des Speicherbereichs geben Sie mit dem dritten Parameter an. Mit dem letzten Parameter können Sie das Verhalten von send() noch beeinflussen. Wird hierbei 0 angegeben, verhält sich send() wie die Systemfunktion write() zum Schreiben. Ansonsten wäre bspw. die symbolische Konstante MSG_OOP ein häufig verwendeter Wert, womit »out-of-band«-Daten gesendet werden können. Weitere flags entnehmen Sie bitte wieder aus der entsprechenden Dokumentation (bspw. Manual-Page) – da hierauf nicht näher eingegangen wird.

Im Falle eines Fehlers liefert send() –1 (was unter MS-Windows gleichwertig zur Konstante SOCKET_ERROR ist) zurück. Welcher Fehler auftrat, lässt sich wieder mit den üblichen betriebssystembedingten Routinen überprüfen (errno unter Linux/UNIX und WSAGetLastError() unter MS-Windows).

Auch wenn kein Fehler auftritt, ist es dennoch sehr wichtig, den Rückgabewert zu überprüfen. Denn bei der Netzwerkprogrammierung sind auch gewisse Grenzen (Bandbreite) vorhanden – sprich Sie können nicht unendlich viele Daten auf einmal versenden. Mit der Auswertung des Rückgabewertes können/müssen Sie sich selber darum kümmern, dass der eventuelle Rest, der nicht gesendet werden konnte, ebenfalls noch verschickt wird. Dies erledigen Sie, indem Sie data_len mit dem Rückgabewert von send() vergleichen. Durch diese Differenz (data_len – Rückgabewert) erhalten Sie die noch nicht gesendeten Daten.

Um Daten von einem Stream-Socket zu empfangen (lesen) wird die Funktion recv() verwendet. Die Syntax unter Linux/UNIX lautet:

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

Und die Syntax unter MS-Windows:

#include <winsock.h>
int recv (SOCKET s, char FAR* data,  int data_len,  int flags);

Auch hier lassen sich mit Ausnahme des letzten Parameters wieder Parallelen zur Systemfunktion read() ziehen. Der erste Parameter ist wieder der Socket-Deskriptor der Verbindung, gefolgt von einem Zeiger auf einen Puffer, in den die Daten gelegt werden sollen. Die Länge des Puffers geben Sie mit dem dritten Parameter an und mit den Flags können Sie das Verhalten von recv() beeinflussen. Eine Angabe von 0 bedeutet auch hier, dass sich recv() wie die Funktion read() verhält. Ansonsten wird auch hierbei gerne die Konstante MSG_OOP (für »out-of-band«-Daten, die gelesen werden können) und MSG_PEEK verwendet. Mit MSG_PEEK können Daten erneut gelesen werden. Zu weiteren möglichen flags sollten Sie bei Bedarf die entsprechende Dokumentation lesen (bspw. Manual-Page).

Im Falle eines Fehlers gilt dasselbe wie schon bei der Funktion send(). Außerdem kann die Funktion recv() auch 0 zurückgeben. Dies bedeutet dann, dass der Verbindungspartner seine Verbindung beendet hat. Ansonsten wird auch mit recv() die Anzahl der erfolgreich gelesenen Bytes zurückgeliefert.

sendto() und recvfrom() – UDP

Für die Funktionen zum Senden und Empfangen von Datagrammen (UDP-Sockets) werden vorzugsweise sendto() und recvfrom() verwendet. Die Syntax unter Linux/UNIX hierzu:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t  recvfrom( int  s,  void  *buf,  size_t  len,
                   int flags, struct sockaddr *from,
                   socklen_t   *fromlen );
ssize_t  sendto( int  s,  const  void  *msg,  size_t  len,
                 int  flags, const struct sockaddr *to,
                 socklen_t tolen );

Und entsprechende ähnliche Syntax unter MS-Windows:

#include <winsock.h>
int sendto( SOCKET s, const char FAR * buf,  int len,
            int flags, const struct sockaddr FAR * to,
            int tolen );
int recvfrom( SOCKET s, char FAR* buf, int len,
              int flags,  struct sockaddr FAR* from,
              int FAR* fromlen );

Die Bedeutung der einzelnen Parameter sowie vom Rückgabewert entsprechen exakt dem von den TCP-Gegenstücken send() und recv(). Hinzugekommen hingegen sind am Ende zwei weitere Parameter. Wobei Sie mit dem fünften Parameter einen Zeiger auf die Adresse des Zielrechners (bei sendto()) bzw. einen Zeiger auf die Adresse des Absenders (bei recvfrom()) übergeben. Die Angaben entsprechen dabei dem Parameter sockaddr von der Funktion connect(). Mit dem letzten Parameter beider Funktionen geben Sie wieder die Größe der Struktur sockaddr an.

Sollten Sie bei einer UDP-Verbindung die connect()-Funktion verwenden, können Sie auch die Funktionen send() und revc() verwenden. In diesem Fall werden die fehlenden Informationen zur Adresse automatisch ergänzt.


Galileo Computing - Zum Seitenanfang

28.4.4 close(), closesocket()  toptop

Sobald Sie mit der Datenübertragung fertig sind, sollten Sie den Socket-Deskriptor wieder freigeben bzw. schließen. Unter Linux/UNIX können Sie hierbei, wie beim Lesen und/oder Schreiben einer Datei, ein simples close() verwenden.

#include <unistd.h>
int close(int s);

Unter MS-Windows hingegen wird hierbei die Funktion closesocket() verwendet, welche letztendlich, abgesehen von einem anderen Funktionsnamen, dieselbe Wirkung erzielt wie ein close() unter Linux/UNIX.

#include <winsock.h>
int closesocket( SOCKET s);

Beide Funktionen erwarten als Parameter den zu schließenden Socket-Deskriptor und geben bei Erfolg 0, ansonsten bei einem Fehler –1 (gleichwertig zu SOCKET_ERROR unter MS-Windows) zurück. Auch hierbei kann man den Fehler anhand von errno (Linux/UNIX) oder der Funktion WSAGetLastError() (MS-Windows) ermitteln. Ein Aufruf von close() bzw. closesocket() beendet außerdem eine TCP-Verbindung sofort.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de