B.13 Netzwerkprogrammierung
B.13.1 Grundlegende Socket-Funktionen
Die gleich folgenden Funktionen zur Socket-Programmierung benötigen alle die Headerdateien:
#include <sys/types.h>
#include <sys/socket.h>
Des-Weiteren wird eine Struktur namens sockaddr verwendet, die wie folgt definiert ist:
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
Diese Struktur lässt sich allerdings recht unbequem ausfüllen (gemeint sind damit Portnummer und Internet-Adresse in der Strukturvariable sa_data), weshalb es für IP-Anwendungen eine spezielle Struktur dafür gibt:
struct sockaddr_in {
sa_family_t sin_family; /* Address-Familie */
unsigned short int sin_port; /* Portnummer */
struct in_addr sin_addr; /* Internet-Adresse */
};
sockaddr_in ermöglicht es Ihnen, die IP-Adresse sowie die Portnummer getrennt einzutragen. Im Speicher sind diese beiden Strukturen kompatibel, es reicht also eine einfache Typumwandlung, um die gewünschten Informationen zu übergeben.
Tabelle B.68
Grundlegende Socket-Funktionen
Syntax
|
Bedeutung
|
int socket ( int domain,
int type,
int protocol );
|
Einen Socket (Endpunkt) zur Kommunikation anlegen
|
int bind (
int s,
const struct sockaddr *name,
int namelen );
|
Ein Socket mit einer Adresse verknüpfen (Server)
|
int listen(int s, int backlog);
|
Am Socket horchen – auf eine Verbindung warten
|
int accept (
int s,
struct sockaddr *addr,
int *addrlen );
|
Eintreffende Verbindungswünsche bearbeiten
|
int connect (
int s,
const struct sockaddr *name,
int namelen );
|
Eine Verbindung zum Server herstellen (Client)
|
ssize_t send (
int s,
const void *msg,
size_t len,
int flags );
|
Versenden von Daten über ein Socket
|
ssize_t sendto (
int s,
const void *msg,
size_t len,
int flags,
const struct sockaddr *to,
int tolen );
|
Versenden von Daten über ein Socket (wird vorwiegend mit dem UDP-Protokoll verwendet)
|
ssize_t recv (
int s,
void *buf,
size_t len,
int flags );
|
Empfangen von Daten über ein Socket
|
ssize_t recvfrom (
int s,
void *buf,
size_t len,
int flags,
struct sockaddr *from,
int *fromlen );
|
Empfangen von Daten über ein Socket (wird vorwiegend mit dem UDP-Protokoll verwendet)
|
int getsockopt (
int s,
int level,
int optname,
void *optval,
int *optlen );
|
Erfragen von Optionen für Sockets
|
int setsockopt (
int s,
int level,
int optname,
const void *optval,
int optlen );
|
Setzen von Optionen für Sockets
|
int socketpair(
int d,
int type,
int protocol,
int sv[2]);
|
Der Aufruf socketpair() generiert zwei unbenannte, miteinander verbundene Sockets. d bezeichnet die Domain, typ den Typ der Sockets und protocol definiert das Protokoll, das nicht angegeben werden muss. Die Deskriptoren der Sockets werden in sv[0] und sv[1] zurückgegeben. Die beiden Sockets können nicht unterschieden werden. Die Sematik der Funktion entspricht dem Syscall pipe().
|
int shutdown(int s, int how);
|
shutdown() beendet eine ganze Voll-Duplex-Verbindung oder einen Teil einer Voll-Duplex-Verbindung, die mit dem Socket s verbunden ist. Ist how=0, so werden weitere empfangene Nachrichten abgelehnt. Ist how=1 , so werden weitere zu sendende Nachrichten abgelehnt. Ist how=2 , so werden zu sendende und empfangene Nachrichten abgelehnt.
|
int getpeername(
int s,
struct sockaddr *name,
int *namelen );
|
getperrname() liefert zu einem gegebenen Socket s die Adresse des entfernten Endes. name ist ein Zeiger auf einen Speicherplatz, an dem das System die Adresse ablegen kann und namelen teilt dem System mit, wie viel Platz vorhanden ist. Daran erkennt es, welche Art von Adresse man erwartet. Da es sich um einen Wert/Ergebnis-Parameter handelt, muss er vorbelegt werden und unbedingt auf einen schreibbaren Speicherplatz zeigen.
|
int getsockname(
int s,
struct sockaddr *name,
int *namelen);
|
getsockname() liefert zu einem gegebenen Socket s die Adresse des lokalen Endes. name ist ein Zeiger auf einen Speicherplatz, an dem das System die Adresse ablegen kann und namelen teilt dem System mit, wie viel Platz vorhanden ist. Daran erkennt es, welche Art von Adresse man erwartet. Da es sich um einen Wert/Ergebnis-Parameter handelt, muss er vorbelegt werden und unbedingt auf einen schreibbaren Speicherplatz zeigen.
|
B.13.2 Konvertieren zur richtigen Byteanordnung
Es gibt verschiedene Architekturen, in denen sich die Anordnung der Bytereihenfolge beim Abspeichern einer Zahl unterscheiden. Generell wird dabei zwischen Big Endian und Little Endian unterschieden. Beim Big Endian wird das höchstwertige Byte an der niedrigsten Adresse gespeichert, das zweithöchste an der nächsten Adressen 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.
Damit sich jetzt aber alle Rechner untereinander verstehen, hat man sich auf eine einheitliche Datenübertragung mit Big Endian (auch als network-byte-order bezeichnet) geeinigt. Um jetzt aus einer lokal verwendeten Byte-Reihenfolge (host-byte-order) eine network-byte-order-Reihenfolge und umgekehrt zu konvertieren, stehen Ihnen die folgenden Funktionen zur Verfügung, welche alle die Headerdatei <netinet/in.h> benötigen.
Tabelle B.69
Funktionen zur Konvertieren der richtigen Byteanordnung
Syntax
|
Bedeutung
|
uint32_t htonl(uint32_t);
|
Konvertiert einen 32 Bit-Wert im host-byte-order zu einem 32 Bit-Wert im network-byte-order
|
uint16_t htons(uint16_t);
|
Konvertiert einen 16 Bit-Wert im host-byte-order zu einem 16 Bit-Wert im network-byte-order
|
uint32_t ntohl(uint32_t);
|
Konvertiert einen 32 Bit-Wert im network-byte-order zu einem 32 Bit-Wert im host-byte-order
|
uint16_t ntohs(uint16_t);
|
Konvertiert einen 32 Bit-Wert im network-byte-order zu einem 32 Bit-Wert im host-byte-order
|
B.13.3 Abfragen von Netzwerknummern und Internetadressen
Zur Verwendung der folgenden Funktionen sind folgende Headerdateien nötig:
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
Des Weiteren wird die Struktur in_addr verwendet, welche sich auch in der Headerdatei sockaddr_in wieder findet:
struct sockaddr_in {
/* Adressfamilie normalerweise AF_INET */
short int sin_family;
/* Port der Verbindung */
unsigned short int sin_port;
/* Adresse zu der Verbunden werden soll */
struct in_addr sin_addr;
/* Fuelldaten um auf 14 Bytes zu kommen */
unsigned char sin_zero[8];
};
struct in_addr{
unsigned long int sin_addr;
}
Tabelle B.70
Funktionen zum Abfragen von Netzwerknummern und Internetadressen
Syntax
|
Bedeutung
|
int inet_aton (
const char *name,
struct in_addr *addr );
|
Konvertiert den String in der Punktierten Dezimaldarstellung (bspw. 127.0.0.1) in eine 32 Bit-Internetadresse.
|
uint32_t inet_network (
const char *name );
|
Extrahiert aus der als punktierten Dezimaldarstellung die Netzwerknummer.
|
char * inet_ntoa (
struct in_addr addr );
|
Konvertiert die übergebene 32 Bit-Adresse, die im network-byte-order vorliegen muss, in eine punktierte Dezimaldarstellung als String.
|
struct in_addr inet_makeaddr (
uint32_t net,
uint32_t local );
|
Generiert aus dem lokalen Teil eines Rechners und einer Netzwerknummer eine numerische IP-Adresse.
|
uint32_t inet_lnaof (
struct in_addr addr );
|
Ermittelt die Adresse des lokalen Teils (bspw. 127.0.0.1 -> der lokale Teil = 0.0.0.1) des Rechners. Die Funktion gibt diesen Wert in Form eines 32 Bit-Wertes im host-byte-order zurück.
|
uint32_t inet_netof (
struct in_addr addr );
|
Konvertiert eine numerische 32 –Bit-Darstellung der IP-Adresse, die als Parameter angegeben wird, in eine Netzwerknummer, die im host-byte-order zurückgegeben wird
|
int inet_pton (
int af,
const char *cp,
void *buf );
|
Wie inet_aton() nur werden damit neben IPv4- auch IPv6-Adressen unterstützt.
|
const char * inet_ntop (
int af,
const void *cp,
char *buf,
size_t len);
|
Wie inet_ntoa() nur werden damit neben IPv4- auch IPv6-Adressen unterstützt.
|
B.13.4 Hostnamen aus der Host-Datenbank ermitteln
Die Funktionen in diesem Abschnitt benötigen die folgenden Headerdateien:
#include <sys/socket.h>
#include <netdb.h>
Die Struktur hostent ist ein Datentyp, der einen Eintrag in der Host-Datenbank repräsentiert und folgende Mitglieder besitzt:
struct hostent {
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
};
Tabelle B.71
Funktionen, um auf die Host-Datenbank zuzugreifen
Syntax
|
Bedeutung
|
struct hostent * gethostbyname (
const char *name );
|
Ermittelt die IP-Adresse (z. B. 192.0.34.72) passend zum angegebenen Internet-Host-Namen (z. B. www.example.com)
|
struct hostent * gethostbyaddr (
const char *addr,
size_t length,
int format );
|
Ermittelt den Internet-Host-Namen (z. B. www.example.com) passend zur angegebenen IP-Adresse (z. B. 192.0.34.72)
|
struct hostent * gethostbyname2(
const char *name,
int af );
|
Ermittelt die IP-Adresse (z. B. 192.0.34.72) passend zum angegebenen Internet-Host-Namen (z. B. www.example.com) – Der neu hinzugekommene Parameter af bezeichnet die gewünschte Adressfamilie AF_INET oder AF_INET6.
|
B.13.5 Service-Datenbank
Die Funktionen in diesem Abschnitt benötigen die folgende Headerdatei:
#include <netdb.h>
Die Struktur servent ist ein Datentyp, der einen Eintrag in der Service-Datenbank repräsentiert und folgende Mitglieder besitzt:
struct servent {
char *s_name; /* official name of service */
char **s_aliases; /* alias list */
int s_port; /* port service resides at */
char *s_proto; /* protocol to use */
};
Tabelle B.72
Funktionen zum Zugriff auf die Service-Datenbank
Syntax
|
Bedeutung
|
struct servent * getservbyname (
const char *name,
const char *proto );
|
Ermittelt einen Port-Wert passend zu einem Internet-Dienst und Protokoll.
|
struct servent * getservbyport (
int port,
const char *proto );
|
Ermittelt einen Internet-Dienst passend zu einem Port und Protokoll.
|
|