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

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
Pfeil 25.1 Begriffe zur Netzwerktechnik
Pfeil 25.1.1 IP-Nummern
Pfeil 25.1.2 Portnummer
Pfeil 25.1.3 Host- und Domainname
Pfeil 25.1.4 Nameserver
Pfeil 25.1.5 Das IP-Protokoll
Pfeil 25.1.6 TCP und UDP
Pfeil 25.1.7 Was sind Sockets?
Pfeil 25.2 Headerdateien zur Socketprogrammierung
Pfeil 25.2.1 Linux/UNIX
Pfeil 25.2.2 Windows
Pfeil 25.3 Client/Server-Prinzip
Pfeil 25.3.1 Loopback-Interface
Pfeil 25.4 Erstellen einer Client-Anwendung
Pfeil 25.4.1 »socket()« – Erzeugen eines Kommunikationsendpunktes
Pfeil 25.4.2 »connect()« – ein Client stellt eine Verbindung zum Server her
Pfeil 25.4.3 Senden und Empfangen von Daten
Pfeil 25.4.4 »close()« und »closesocket()«
Pfeil 25.5 Erstellen einer Server-Anwendung
Pfeil 25.5.1 »bind()« – Festlegen einer Adresse aus dem Namensraum
Pfeil 25.5.2 »listen()« – Warteschlange für eingehende Verbindungen einrichten
Pfeil 25.5.3 »accept()« und die Serverhauptschleife
Pfeil 25.6 (Cross-Plattform-)TCP-Echo-Server
Pfeil 25.6.1 Der Client
Pfeil 25.6.2 Der Server
Pfeil 25.7 Cross-Plattform-Development
Pfeil 25.7.1 Abstraction Layer
Pfeil 25.7.2 Headerdatei für Linux/UNIX
Pfeil 25.7.3 Linux/UNIX-Quellcodedatei
Pfeil 25.7.4 Headerdatei für MS-Windows
Pfeil 25.7.5 Windows-Quellcodedatei
Pfeil 25.7.6 All together – die »main«-Funktionen
Pfeil 25.7.7 Ein UDP-Beispiel
Pfeil 25.7.8 Mehrere Clients gleichzeitig behandeln
Pfeil 25.8 Weitere Anmerkungen zur Netzwerkprogrammierung
Pfeil 25.8.1 Das Datenformat
Pfeil 25.8.2 Der Puffer
Pfeil 25.8.3 Portabilität
Pfeil 25.8.4 Von IPv4 nach IPv6
Pfeil 25.8.5 RFC-Dokumente (Request for Comments)
Pfeil 25.8.6 Sicherheit


Rheinwerk Computing - Zum Seitenanfang

25.8 Weitere Anmerkungen zur Netzwerkprogrammierung Zur nächsten ÜberschriftZur vorigen Überschrift

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 ich hier beschrieben habe. Dazu könnte (will) man ein ganzes Buch schreiben (an mir soll‘s nicht liegen ;-)). Bevor Sie sich jetzt in das Abenteuer stürzen, eigene kleine Programme mit Netzwerkfunktionalität zu schreiben, möchte ich Sie hier noch in ein paar Abschnitten auf einige Dinge hinweisen, auf die Sie besonders achten sollten (bzw. mit denen Sie sich noch intensiver auseinandersetzen sollten) – insbesondere, wenn Ihre Anwendung nicht so funktioniert, wie Sie es gerne hätten.


Rheinwerk Computing - Zum Seitenanfang

25.8.1 Das Datenformat Zur nächsten ÜberschriftZur vorigen Überschrift

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 beispielsweise Ganzzahlen oder Gleitpunktzahlen 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, dass Sie nicht immer wissen, 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 64-Bit-Rechner einen Integer an einen 32-Bit-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 beispielsweise 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 (insbesondere 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, auf dem sich nur japanische Schriftarten befinden, kommt nichts dabei raus. Ebenso müssen Sie unsere landestypischen Umlaute berücksichtigen, die meist auf einem Rechner, der beispielsweise in der USA steht, auch nicht richtig dargestellt werden können. Der Nachteil: Es wird Bandbreite verschwendet. Eine 64-Bit-Nummer beispielsweise 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!


Rheinwerk Computing - Zum Seitenanfang

25.8.2 Der Puffer Zur nächsten ÜberschriftZur vorigen Überschrift

Bisher mussten Sie sich nie so richtig um die Pufferung der Daten kümmern. Beispielsweise hat Ihnen bei Funktionen wie fgets() oder fputs() das System die Pufferung abgenommen. Sie mussten hierbei nur angeben, wie groß dieser Puffer 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 als 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 dann, 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. Wenn Sie etwas in einen Puffer schreiben wollen, sollten Sie immer bedenken, wie es auf der anderen Seite wieder herauskommt, und eventuell auch überprüfen, was herauskommt. Denn wenn etwas häufig 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.



Rheinwerk Computing - Zum Seitenanfang

25.8.3 Portabilität Zur nächsten ÜberschriftZur vorigen Überschrift

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 andere Systeme gibt. Gemeint sind beispielsweise 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 gegebenenfalls schlau machen, welche Differenzen es dabei gibt.


Rheinwerk Computing - Zum Seitenanfang

25.8.4 Von IPv4 nach IPv6 Zur nächsten ÜberschriftZur vorigen Überschrift

Da IPv6 noch nicht eingeführt wurde (und eine Einführung noch nicht in Sicht ist), wurden seine Eigenheiten in den vorangegangenen Abschnitten nicht näher behandelt. Allerdings gibt es hierzu eigentlich auch gar nicht viel zu berichten. Daher folgt hier eine kurze Zusammenfassung für den Fall der Fälle, die zeigt, 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. Hierbei muss 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, das 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. Sie 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 (siehe oben), ist es auch nötig, die Struktur sockaddr_in anzupassen:

struct sockaddr_in6 {
   sa_family_t sin6_family     /* Adress-Familie - AF_INET6 */
   in_port_t sin6_port;        /* Port Transportschicht # */
   uint32_t sin6_flowinfo;     /* IPv6 Datenfluss-Informationen */
   struct in6_addr sin6_addr;  /* IPv6 Adresse */
   uint32_t sin6_scope_id;     /* IPv6 Scope-Kennung */
};

Hier wurde nicht nur die Adressstruktur verändert, es wurden auch 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 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 auch beliebige Adressfamilien.

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, bei der 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.



Rheinwerk Computing - Zum Seitenanfang

25.8.5 RFC-Dokumente (Request for Comments) Zur nächsten ÜberschriftZur vorigen Überschrift

Sie wollen einen HTTP-, einen FTP- oder einen SMTP-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 über Protokolle statt (vergleichbar mit den verschiedenen Sprachen dieser Welt). Ein Webclient und ein Webserver beispielsweise unterhalten sich anders als ein Mailclient und ein Mailserver. All diese Standard-Protokolle werden in den RFCs (Requests for Comments) gesammelt. RFCs sind eine Reihe von technischen Dokumentationen zum Internet, die 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).


Rheinwerk Computing - Zum Seitenanfang

25.8.6 Sicherheit topZur vorigen Überschrift

Das Wichtigste kommt zum Schluss. Mit der Netzwerkprogrammierung in C haben Sie auch das gefährlichste Kapitel in C kennengelernt. 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. Beispielsweise kann ein Buffer-Overflow bei einer Netzwerk-Anwendung sehr böse Folgen haben (ein beliebtes Lästerbeispiel ist der Internet Explorer alias »Internet Exploiter«).

Des Weiteren sollten Sie beachten, dass die Daten, die Sie über ein Netzwerk versenden, jederzeit abgefangen werden können. Daher empfiehlt es sich, bei sicherheitsrelevanten Daten (beispielsweise 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.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: C von A bis Z

 C von A bis Z
Jetzt bestellen


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

 Buchtipps
Zum Rheinwerk-Shop: C/C++






 C/C++


Zum Rheinwerk-Shop: Einstieg in C






 Einstieg in C


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


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




Copyright © Rheinwerk Verlag GmbH 2009
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