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.10 Namen und IP-Adressen umwandeldowntop

Zwar wissen Sie jetzt, dass im Internet alles über IP-Nummern erreichbar ist, aber wenn Sie sich mit diesen Nummern durchs WWW hangeln müssten, wäre das Internet wohl heute nicht so erfolgreich. Deshalb wird an die Nummern noch zusätzlich ein Name, bestehend aus Host- und Domainname (und eventuell einer Top-Level-Domain (TLD)), vergeben (hostname.domainname.tld; z. B. www.pronix.de).

Der Domainname ist der Name, womit Sie eine Webseite (z. B. pronix.de) im Internet ansprechen. Ein Domainname wird von hinten nach vorne aufgelöst. Somit steht das de für die Top-Level-Domain (wie der Name sagt, das, was ganz vorne steht). Die Top-Level-Domain bezeichnet die Herkunft und/oder die Zugehörigkeit (.com; .gov; .net etc.), was hier mit de für Deutschland steht. Nach der Top-Level-Domain folgt der eigentliche Domainname, der im Beispiel pronix lautet.

Der Hostname ist somit die niedrigste Instanz eines Domainnamens, da dieser ja von hinten nach vorne aufgelöst wird (aber der erste Teil in der Leseweise). Gewöhnlich lautet im Internet der Hostname www (www.pronix.de) – allerdings spricht nichts dagegen, diesen auch anders zu benennen. Es wird nämlich nur empfohlen, weil sich viele Anwender an das www gewöhnt haben. Genauso gut können Sie auch abc.pronix.de verwenden.

Die Vorteile, Namen anstatt Nummern zu verwenden, sind eindeutig:

gp  Einfacher les- und verwendbar.
gp  Die Nummern können verändert werden, und der Name kann trotzdem gleich bleiben, wie dies z. B. im Fall der Umstellung von IPv4 auf IPv6 ohne weiteres möglich wäre/ist. Es müssen lediglich dem Namen entsprechende Nummern zugeteilt werden.
gp  Mehrere Nummern können auch zu einem Ziel führen. Geben Sie z. B. www.pronix.de an und die Nummer lautet 193.129.2.1 – aber die Seite ist überlaufen -, so ist es ohne Problem möglich, auf eine andere IP-Nummer zu wechseln, die zur selben Adresse führt. Also mehrere Nummern können für den gleichen Host vergeben werden (wie dies z. B. bei Webhostern der Fall ist).

Rheinwerk Computing

11.10.1 Name-Server  downtop

Einfach ausgedrückt ist ein Name-Server ein Rechner, der für die Umsetzung von Rechnernamen zur IP-Nummer verantwortlich ist. Im Internet z. B. ist dies vergleichbar mit einem Telefonbuch, wo der Name des Teilnehmers in die Telefonnummer aufgelöst wird. Der Dienst, der Ihnen diese Arbeit im Internet abnimmt, wird als Domain Name System (DNS) bezeichnet. Bei kleineren Netzwerken wie z. B. dem Intranet werden die lokalen IP-Nummern meistens in Form einer Tabelle in einer Datei (/etc/hosts) hinterlegt. Größere Netzwerke, wie das Internet eines ist, halten diese Daten meistens in eigenen Datenbanken. Der Name des lokalen Name-Servers finden Sie in der Datei /etc/resolv.conf. Kennt dieser Name-Server keinen Namen und die dazugehörende IP-Adresse, fragt dieser im Internet bei anderen Name-Servern nach.


Rheinwerk Computing

11.10.2 Informationen zum Rechner im Netz – gethostbyname und gethostbyaddr  downtop

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 (z. B. 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 );

Die Funktion gethostbyname() gibt bei Erfolg einen Zeiger auf struct hostent des gefundenen Rechners zurück, ansonsten bei einem Fehler NULL.

Um aus einem Rechnernamen eine IP-Adresse und weitere Informationen zu ermitteln, steht (wie bereits erwähnt) der Name-Server zur Verfügung – dieser Rechner ist für die Umsetzung zwischen Rechnernamen und IP-Nummern zuständig. Selbst auf Ihrem Rechner finden Sie, wie schon erwähnt, 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; 
 }; 

Kurze Beschreibung der einzelnen Strukturvariablen:


Tabelle 11.3    Die Strukturvariablen in struct hostent

Strukturvariable Bedeutung
h_name Offizieller Name des Rechners im Netz
h_aliases Ein Stringarray, worin sich eventuelle Aliasnamen befinden. Das letzte Element ist immer NULL.
h_addrtyp Hier steht der Adresstyp, was gewöhnlich AF_INET für IPv4 oder AF_INET6 für IPv6 ist.
h_length Hier befindet sich die Länge der nummerischen Adresse (4 oder 16) in Bytes.
h_addr_list Hierbei handelt es sich um ein Array von Zeigern auf die Adressen von IPv4-/IPv6-Nummern in der Netzwerk-Byte-Reihenfolge (network byte order).

Benötigen Sie hingegen von einer IP-Adresse Informationen zu einem Rechner, können Sie die Funktion gethostbyaddr() verwenden. Diese Funktion gibt bei Erfolg ebenfalls einen Zeiger auf die Struktur hostent des gefundenen Rechners zurück oder bei einem Fehler NULL.


Hinweis   Dies wird allerdings nur funktionieren, wenn es sich um einen FQDN (Fully Qualified Domain Name) handelt, da sonst die Name-Server keinen Namen aus der IP auflösen können. FQDN ist ein eindeutiger Name eines Internet-Hosts. Er setzt sich aus dem Hostnamen und dem Namen der Domain zusammen. Mithilfe des FQDN kann per DNS die IP-Adresse des Hosts ermittelt werden. Z. B.: Mit linux.pronix.de wäre linux der Hostname und pronix.de der Domainname.


#include <netdb.h>
struct hostent *gethostbyaddr( const char *ip_addr, 
                               int len, int type );

Mit dem ersten Parameter geben Sie die IP-Adresse des Zielrechners an. Mit dem zweiten wird die Länge dieser Adresse angegeben, und der letzte Parameter gibt den Typ der Adresse an, der auch hier meistens AF_INET für IPv4 bzw. AF_INET6 für IPv6 ist.

Beachten Sie bitte, dass beide Funktionen (gethostbyname() und gethostbyaddr()) nur einen statischen Speicherbereich für den Rückgabewert reservieren. Jeder nachfolgende Funktionsaufruf überschreibt diesen Speicherplatz wieder. GNU bietet auch gethostbyname_r() und gethostbyaddr_r() für Thread-safe-Aktionen an.


Hinweis   Die Funktionen gethostbyname() und gethostbyaddr() benutzen für ihre Anfragen den Name-Server named, die Datei /etc/hosts und den Network Information Service (NIS oder YP). Was davon und in welcher Reihenfolge benutzt wird, bestimmt die order-Zeile in der Datei /etc/host.conf Siehe dazu auch resolv. Das Standardverhalten ist, zuerst den Name-Server zu befragen und danach die Datei /etc/hosts zu durchsuchen.


Wenn ein Fehler auf der Suche nach einem Rechner mit den Funktionen gethostbyaddr() und gethostbyname() auftritt, sollten Sie die Funktion herror() aufrufen, die ähnlich wie perror() ist, um eine sinnvolle Fehlermeldung zu erhalten, warum ein Fehler aufgetreten ist. Die herror()-Funktion gibt die zum aktuellen Wert von h_errno gehörende Fehlermeldung auf stderr aus.

#include <netdb.h>
extern int h_errno;
void herror ( const char *string );

Die Variable h_errno kann folgende Werte annehmen:


Tabelle 11.4    Mögliche Werte der Fehlervariablen h_errno

Fehlerwert Bedeutung
0 Kein Fehler aufgetreten.
HOST_NOT_FOUND Der angegebene Rechner ist unbekannt.
NO_ADDRESS Der angegebene Name ist gültig, aber es existiert dazu keine IP-Adresse.
NO_RECOVERY Ein nicht behebbarer Name-Server-Fehler ist aufgetreten.
TRY_AGAIN Beim zuständigen Name-Server ist ein vorübergehender Fehler aufgetreten. Versuchen Sie es später nochmals.
NO_DATA Name ist keiner Adresse zugeordnet.

Falls also auf Ihrem System keine Funktion wie herror() vorhanden ist, können Sie mit den Fehlercodes von h_error ohne Probleme eine eigene Fehlerausgaberoutine nachimplementieren.

Das folgende Beispiel soll demonstrieren, wie Sie Informationen zu einem Rechner durch Angabe eines Rechnernamens oder einer IP-Adresse aus der Struktur hostent herausfinden können.

/* hostent.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main(int argc, char **argv) {
  struct hostent *host;
  struct in_addr ip, **ip_ptr;
  char **ptr;
  if( argc != 2 )
    printf("Usage: %s (Rechnername oder IP-Adresse)\n",
       *argv);
  else {
    /* Argument als IP-Adresse ...? */
    if( inet_aton( argv[1], &ip ) != 0 )
      host=gethostbyaddr( (const void *)&ip,
                          sizeof(ip),AF_INET );
    /* Argument als Rechnername */
    else
      host = gethostbyname( argv[1] );
    if( host == NULL ) {
       herror("Konnte Rechner nicht finden");
       return EXIT_FAILURE;
    }
    printf("Hostname    : %s\n", host->h_name);
    printf("Aliase      :\n");
    ptr=host->h_aliases;
    while( *ptr != NULL ) {
       printf("\t%s\n", *ptr++);
    }
    printf("IP-Adressen :\n");
    ip_ptr = (struct in_addr **)host->h_addr_list;
    while(*ip_ptr != NULL) {
       printf("\t%s\n", inet_ntoa( **ip_ptr++));
    }
  }
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o hostent hostent.c
$ ./hostent google.de
Hostname    : google.de
Aliase      :
IP-Adressen :
        216.239.33.100
        216.239.37.100
        216.239.39.100
$ ./hostent www.pronix.de
Hostname    : www.pronix.de
Aliase      :
IP-Adressen :
        194.150.178.34
$ ./hostent 194.150.178.34
Hostname    : goliath.speedpartner.de
Aliase      :
IP-Adressen :
        194.150.178.34
$ ./hostent www.microsoft.com
Hostname    : www.microsoft.com.nsatc.net
Aliase      :
        www.microsoft.com
IP-Adressen :
        207.46.19.30

Hinweis   Wie schon einmal erwähnt, wird die Funktion gethostbyname() vorwiegend bei Clientanwendungen verwendet, um aus der Angabe eines Rechnernamens eine IP-Adresse zu machen.



Rheinwerk Computing

11.10.3 Service-Informationen – getservbyname() und getservbyport()  toptop

Eine weitere Information, die eine Clientanwendung häufig benötigt, ist die Nummer des Ports, der den gewünschten Service verwendet. Oder wissen Sie auf Anhieb zu allen Portnummern den entsprechenden Service? Natürlich könnten Sie in /etc/services nachschauen und die Nummer dann fest einkompilieren. Wie schon bei den Informationen zu den Rechnern im Netz gibt es auch hierfür eine Struktur in der Headerdatei <netdb.h>, worin die Informationen zu den entsprechenden Diensten stehen.

struct servent {
     char *s_name;
     char **s_aliases; 
     int  s_port;
     char *s_proto; 
  };

Eine kurze Beschreibung der einzelnen Strukturvariablen:


Tabelle 11.5    Die Strukturvariablen in servent und deren Bedeutung

Strukturvariable Bedeutung
s_name Offizieller Servicename
s_aliases Ein Stringarray mit eventuellen Aliasnamen zum Service, falls vorhanden. Das letzte Element in der Liste ist NULL.
s_port Die Portnummer zum Servicenamen
s_proto Der Name des verwendeten Protokolls

Hier die Syntax zu getservbyname():

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

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

Ähnlich verhält es sich mit der Funktion getservbyport() (im Vergleich mit getservbyname()). Nur liefert getservbyport() zum Dienst mit der Portnummer port (network-byte-order) und dem Protokoll proto die struct servent-Informationen zurück.

#include <netdb.h>
struct servent *getservbyport(int port, const char *proto);

Hierzu ein Beispiel, das die Funktion getservbyname() demonstriert. Da meistens Informationen abgefragt werden, die das TCP-Protokoll betreffen, könnten Sie auch das zweite Argument der Funktion getserbyname() gleich mit der Stringkonstante "tcp" belegen.

/* servent.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
int main (int argc, char **argv) {
  struct servent *service_info;
  char **ptr;
  if( argc != 3) {
    printf("Usage: %s dienst protokoll\n", *argv);
    return EXIT_FAILURE;
  }
  service_info = getservbyname( argv[1], argv[2] );
  if( service_info == NULL ) {
    herror("Konnte den Service nicht finden\n");
    return EXIT_FAILURE;
  }
  printf("Service   : %s\n", service_info->s_name);
  printf("Port      : %d\n", ntohs( service_info->s_port) );
  printf("Aliase    : \n");
  ptr = service_info->s_aliases;
  while(*ptr != NULL) {
     printf("%s\n", *ptr++);
  }
  printf("Protokoll : %s\n", service_info->s_proto);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o servent servent.c
$ ./servent http tcp
Service   : http
Port      : 80
Aliase    :
(null)
Protokoll : tcp
$ ./servent ftp tcp
Service   : ftp
Port      : 21
Aliase    :
(null)
Protokoll : tcp
$ ./servent pop3 tcp
Service   : pop3
Port      : 110
Aliase    :
(null)
Protokoll : tcp

Dasselbe Beispiel auch mit der Funktion getservbyport(). Als Protokoll verwenden Sie hierbei gewöhnlich TCP. Beim Beispiel werden entsprechende Informationen von den Ports 1 bis 100 (ohne den Alias) ausgegeben:

/* servent2.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main (int argc, char **argv) {
  struct servent *service_info;
  int port = 0;
  while (port++ < 100) {
    service_info = getservbyport (htons (port), "tcp");
    if (service_info == NULL)
       printf ("Port %d : Keine Infos\n", port);
    else {
       printf ("Service : %10s ", service_info->s_name);
       printf ("Port    : %3d ", 
          ntohs (service_info->s_port));
       printf ("Protokoll : %5s\n", service_info->s_proto);
    }
  }
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o servent2 servent2.c
$ ./servent2
Service :     tcpmux Port     : 1  Protokoll :   tcp
Service : compressnet Port    : 2  Protokoll :   tcp
Service : compressnet Port    : 3  Protokoll :   tcp
Port 4  : Keine Infos
Service :        rje Port    :  5  Protokoll :   tcp
Port 6  : Keine Infos
Service :       echo Port    :  7  Protokoll :   tcp
Port 8  : Keine Infos
Service :    discard Port    :  9  Protokoll :   tcp
Port 10 : Keine Infos 
Service :     systat Port    :  11  Protokoll :  tcp
Port 12 : Keine Infos
Service :    daytime Port    :  13  Protokoll :  tcp
Port 14 : Keine Infos 
Service :    netstat Port    :  15  Protokoll :  tcp 
...
... 
Service :  swift-rvf Port    :  97 Protokoll :   tcp
Service :    tacnews Port    :  98 Protokoll :   tcp
Service :   metagram Port    :  99 Protokoll :   tcp
Service :    newacct Port    : 100 Protokoll :   tcp
 << 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