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.8 Weitere Anmerkungen zur Netzwerkprogrammierung  downtop

Mit diesem kleinen Kapitel haben Sie sich jetzt den Grundstein zur Netzwerkprogrammierung gelegt. Bedenken Sie aber, dass dieses Thema ein noch viel größeres Spektrum umfasst, als hier beschrieben. Dazu könnte (will) man ein ganzes Buch schreiben (an mir solls nicht liegen ;-)). Bevor Sie sich jetzt in das Abenteuer stürzen, eigene kleine Programme mit Netzwerkfunktionalität zu schreiben, möchte ich Ihnen hier noch in kleinen Abschnitten einige Dinge hervorheben, auf die Sie besonders ein Augenmerk legen sollten (bzw. mit denen Sie sich noch intensiver auseinander setzen sollten) – insbesondere, wenn Ihre Anwendung nicht so funktioniert, wie Sie es gerne hätten.


Galileo Computing - Zum Seitenanfang

28.8.1 Das Datenformat  downtop

In den Beispielen, die Sie hier erstellt haben, wurden lediglich Zeichenketten verschickt. Meistens liegen die Daten aber nicht in einem solch bequemen Format vor. Wenn Sie bspw. Ganzzahlen oder Gleitkommazahlen versenden wollen, verwenden Sie am besten sscanf() und snprintf().

Und was ist mit binären Strukturen (struct)? Auch hier empfiehlt es sich, die komplette Struktur in eine Zeichenkette zu konvertieren, bevor Sie diese versenden. Auf der anderen Seite müssen selbstverständlich ebenfalls bestimmte Vorkehrungen getroffen werden.

Sicherlich, letztendlich entscheiden Sie, wie die Daten zwischen Client und Server hin- und hergeschickt werden. Allerdings sollten Sie bedenken, Sie wissen nicht immer, auf was für einen Rechner die Daten übertragen werden. Schicken Sie von einem Little-Endian-Rechner einen Integer an ein Big-Endian-System, sind Probleme vorprogrammiert. Oder was ist, wenn Sie von einem 64Bit-Rechner einen Integer an einen 32Bit-Rechner verschicken? Sie wissen also nie genau, welche Größe die Datentypen int, long und short auf der Gegenseite haben. Zwar gibt es (seit der Einführung des C99-Standards) die unabhängigen Typen wie bspw. int8_t, int16_t etc. und uint8_t etc. in der Headerdatei stdint.h, was aber recht wenig nützt, weil dieser Standard von einigen Compiler-Herstellern (insbes. auf Windows-Systemen) unzureichend bis überhaupt nicht beachtet wird.

Daher die Empfehlung: Senden Sie numerische Daten immer im Textformat an die Gegenseite. Natürlich setzt dies voraus, dass die Gegenseite denselben Zeichensatz verwendet. Wenn Sie einen String an einen Rechner schicken, worauf sich nur japanische Schriftarten befinden, kommt nichts dabei raus. Ebenso müssen Sie unsere landestypischen Umlaute berücksichtigen, welche meist auf einem Rechner, der bspw. in der USA steht, auch nicht richt dargestellt werden können. Der Nachteil: Es wird Bandbreite verschwendet. Eine 64Bit-Nummer bspw. kann nämlich über 20 Zeichen lang sein, während im Binärformat gerade mal 8 Zeichen dafür benötigt werden. Aber es ist Ihre Entscheidung!


Galileo Computing - Zum Seitenanfang

28.8.2 Der Puffer  downtop

Bisher mussten Sie sich nie so richtig um die Pufferung der Daten kümmern. Bspw. bei Funktionen wie fgets() oder fputs() hat Ihnen das System die Pufferung abgenommen. Sie mussten hierbei nur angeben, wie groß dieser sein sollte. In der Netzwerkprogrammierung müssen Sie sich nun selbst darum kümmern. Mit den Funktionen send()/sendto() und recv()/recvfrom() können bei den Sockets erst mal weniger Bytes ein- bzw. ausgegeben werden wie angenommen. Das Problem ist, dass das System (der Kernel) für das Socket eine bestimmte Puffergröße vorgibt. Das bedeutet, wenn der Puffer voll ist, liest recv()/recvfrom() bzw. schreibt send()/sendto() aus diesem bzw. in diesen Puffer – selbst wenn noch nicht alle gewünschten Daten ausgelesen bzw. geschrieben wurden.

Wenn Sie die Daten in Form eines char-Arrays mit einfachem Text übertragen, dürften Sie keine Probleme mit der Pufferung bekommen, sofern Sie einen String ordentlich mit \0 abschließen. Sobald allerdings binäre Daten übertragen werden sollen, gibt es Probleme damit. Bei binären Daten können Sie sich nicht darauf verlassen, dass diese mit einem \0 abgeschlossen werden – weshalb Sie sich hierbei selbst um das letzte Zeichen kümmern müssen.

Welche Puffergröße Sie verwenden, bleibt Ihnen überlassen und hängt vom Anwendungsfall ab. Allerdings macht ein byteweiser Puffer genauso wenig Sinn wie ein überdimensional großer Puffer. Es hat sich bewährt, eine Puffergröße von 512 oder 1024 KB zu verwenden.


Hinweis   Der Puffer ist Ihr wichtigstes Kommunikationswerkzeug, mit dem Sie Daten austauschen können. Sie sollten daher immer bedenken, wenn Sie etwas darin hineinschreiben, wie dies auf der anderen Seite wieder herauskommt, und eventuell auch überprüfen, was herauskommt. Denn, wenn häufig etwas nicht klappt, dann ist es die Art und Weise, wie die Daten beim Empfänger ankommen. Nicht selten ist es ein nicht terminierter String, der für Zeichensalat sorgt.



Galileo Computing - Zum Seitenanfang

28.8.3 Portabilität  downtop

Sie haben in diesem Kapitel gesehen, wie man mit einem abstrakten Layer eine portable Anwendung (nicht nur) für die Netzwerkprogrammierung erstellen kann. Mit Linux/UNIX und MS-Windows haben Sie in den Beispielen eine recht große Zielgruppe eingeschlossen. Bedenken Sie allerdings, dass es auch noch Systeme jenseits dieser gibt. Gemeint sind bspw. Systeme wie QNX oder SGI IRIX. Zwar sind die Unterschiede der allgemeinen Socket-Programmierung nicht allzu gravierend, dennoch müssen Sie sich auch diesbezüglich ggf. schlau machen, welche Differenzen es dabei gibt.


Galileo Computing - Zum Seitenanfang

28.8.4 Von IPv4 nach IPv6  downtop

Da IPv6 noch nicht eingeführt wurde (und eine Einführung noch nicht in Sicht ist) wurden die Eigenheiten in den vorangegangenen Abschnitten nicht näher behandelt. Allerdings gibt es hierzu eigentlich auch gar nicht die Welt zu berichten. Daher hier eine kurze Zusammenfassung, für den Fall der Fälle, wie Sie Ihre Anwendungen von IPv4 nach IPv6 portieren könnten.

Konstanten

Die IPv4-Konstanten AF_INET bzw. PF_INET wurden durch AF_INET6 bzw. PF_INET6 ersetzt. Es muss hierbei eigentlich nur die Konstante um eine 6 erweitert werden. Es ist auch kein Fehler, wenn Sie auch bei einer IPv4-Software gleich die neuen Konstanten verwenden, da ein Programm, welches auf IPv6 portiert wurde, auch weiterhin auf IPv4-Rechnern läuft (vorausgesetzt, der Rechner ist »dual-stacked«, was in Zukunft bei IPv6-fähigen Rechnern immer der Fall sein sollte).

Was sich auch verändert hat, ist die Konstante INADDR_ANY, die beim Binden von Sockets an einen Port angeben wird, und die bedeutet, dass Pakete von jedem Interface angenommen werden. Ein wenig ungewöhnlich ist, dass die neue Konstante kleingeschrieben wird – in6addr_any. Der Grund hierfür: Die alte Struktur in_addr bestand nur aus einem "unsigned long int s_addr" und somit war die Konstante INADDR_ANY auch nur eine Zahl. Da die Adresse bei IPv6 128 Bit breit ist, ist dies nicht mehr möglich (da kein portabler Datentyp mit dieser Breite existiert), weshalb es sich nun um ein Array handelt:

struct in6_addr {
   union  {
      uint8_t  u6_addr8[16];
      uint16_t u6_addr16[8];
      uint32_t u6_addr32[4];
   } in6_u;
#define s6_addr    in6_u.u6_addr8
#define s6_addr16  in6_u.u6_addr16
#define s6_addr32  in6_u.u6_addr32
};

Strukturen

Nachdem in_addr durch in6_addr ersetzt wurde (s.o.), ist es auch nötig, die Struktur sockaddr_in anzupassen:

struct sockaddr_in6 {
   sa_family_t sin6_family     /* Address family – AF_INET6 */
   in_port_t sin6_port;        /* Transport layer port # */
   uint32_t sin6_flowinfo;     /* IPv6 flow information */
   struct in6_addr sin6_addr;  /* IPv6 address */
   uint32_t sin6_scope_id;     /* IPv6 scope-id */
};

Außer, dass die Adressstruktur verändert wurde, wurden noch die zusätzlichen Strukturvariablen sin6_flowinfo und sin6_scope_id hinzugefügt.

Funktionen

Der Großteil der Socket-API-Funktionen ist gleich geblieben. Verändert (hinzugefügt) wurden lediglich die meisten Adressauflösungs- und Konvertierungsfunktionen. So werden die Funktionen (wurde bereits im Buch erwähnt) inet_aton() bzw. inet_ntoa() durch die Funktionen inet_pton() bzw. inet_ntop() ersetzt. Da diese neuen Funktionen jetzt nicht mehr auf Zahlen operieren, sondern auf den konkreten Adressstrukturen (z.B. in6_addr), unterstützen sie somit auch beliebige Adress-Familien.

Noch wichtiger sind die neu hinzugekommenen Funktionen getaddrinfo() und getnameinfo(). Diese wurden als Ersatz für die Funktionen gethostbyname()/gethostbyaddr() und getipnodebyname()/getipnodebyaddr() eingeführt und haben den Vorteil, dass sie direkt sockaddr-Strukturen bearbeiten. Des Weiteren wurde noch die Funktion gethostbyname2() hinzugefügt, wobei es sich allerdings nur um eine reine GNU-Extension handelt!


Hinweis   All diese Funktionen stehen Ihnen übrigens auch schon für IPv4 zur Verfügung, weshalb es nicht falsch sein kann, diese jetzt schon zu verwenden, um eine eventuell spätere Portierung zu erleichtern.



Galileo Computing - Zum Seitenanfang

28.8.5 RFC-Dokumente (Request for Comments)  downtop

Sie wollen einen HTTP-, einen FTP- oder einen SMPT-Server bzw. einen Client erstellen, der damit kommuniziert, und wissen nicht, wo Sie anfangen sollen. Dies ist eine beliebte Frage in den Foren. Wie Sie bereits erfahren haben, findet die Kommunikation zwischen dem Server und Client mit Protokollen statt (vergleichbar mit den verschiedenen Sprachen dieser Welt). Ein Webclient und ein Webserver bspw. unterhalten sich anders als ein Mailclient und ein Mailserver. All diese Standard-Protokolle werden unter dem Namen RFC (Request for Comments) gesammelt. RFCs sind eine Reihe von technischen Dokumentationen zum Internet, welche ihren Ursprung zu ARPANET-Zeiten 1969 hatten. Einen gewaltigen Fundus zur RFC-Sammlung finden Sie im Internet unter http://www.ietf.org/ (The Internet Engineering Task Force).


Galileo Computing - Zum Seitenanfang

28.8.6 Sicherheit  toptop

… und dass Wichtigste zum Schluss. Mit den Kenntnissen der Netzwerkprogrammierung in C haben Sie auch das gefährlichste Kapitel in C kennen gelernt. Die meisten Programme, die angegriffen werden, sind nicht Ihr Editor oder Ihre Entwicklungsumgebung, sondern die Programme, die mit dem Netz verbunden und somit meistens auch für ALLE erreichbar sind. Ein Buffer Overflow bspw. bei einer Netzwerk-Anwendung kann sehr böse Folgen haben (beliebtes Lästerbeispiel der Internet Explorer (alias Internet Exploiter)).

Des Weiteren sollten Sie beachten, dass die Daten, die Sie übers Netzwerk versenden, jederzeit abgefangen werden können. Daher empfiehlt es sich, bei sicherheitsrelevanten Daten (bspw. Kundendaten) diese verschlüsselt zu versenden. Die Daten können zwar weiterhin abgefangen werden, aber bei einer guten Verschlüsselung sind diese Daten für den »Sniffer« nicht mehr lesbar – es sei denn, er kann die Verschlüsselung knacken.

 << 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