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.6 (Cross-Plattform)TCP-Echo-Server  downtop

Hierzu erfolgt jetzt als Beispiel ein einfacher portabler TCP-Echo-Server mit fast allen Funktionen, die eben beschrieben wurden. Befindet sich bspw. die Server-Software auf einem Rechner mit der IP-Adresse 196.12.32.6, so können Sie mit der Clientanwendung unter Angabe der entsprechenden IP-Adresse einen einfachen String via Kommandozeile senden. Der Server gibt diesen String mitsamt der Herkunft des Clients (IP-Adresse) und des Datums mit Uhrzeit auf die Standardausgabe aus.


Galileo Computing - Zum Seitenanfang

28.6.1 Der Client  downtop

Hierzu der Quellcode der Clientanwendung:

/* client.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef _WIN32
/* Headerfiles für Windows */
#include <winsock.h>
#include <io.h>
#else
/* Headerfiles für UNIX/Linux */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#define PORT 1234
#define RCVBUFSIZE 8192
/* Funktion gibt aufgetretenen Fehler aus und
 * beendet die Anwendung */
static void error_exit(char *errorMessage) {
#ifdef _WIN32
    fprintf(stderr,"%s: %d\n", errorMessage, WSAGetLastError());
#else
    fprintf(stderr, "%s: %s\n", errorMessage, strerror(errno));
#endif
    exit(EXIT_FAILURE);
}
int main( int argc, char **argv) {
    struct sockaddr_in server;
    struct hostent *host_info;
    unsigned long addr;
#ifdef _WIN32
    SOCKET sock;
#else
    int sock;
#endif
    char *echo_string;
    int echo_len;
#ifdef _WIN32
    /* Initialisiere TCP für Windows ("winsock") */
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD (1, 1);
    if (WSAStartup (wVersionRequested, &wsaData) != 0)
        error_exit( "Fehler beim Initialisieren von Winsock");
    else
        printf("Winsock initialisiert\n");
#endif
 /* Sind die erforderlichen Kommandozeilenargumente vorhanden? */
    if (argc < 3)
        error_exit("usage: client server-ip echo_word\n");
    /* Erzeuge das Socket */
    sock = socket( AF_INET, SOCK_STREAM, 0 );
    if (sock < 0)
        error_exit( "Fehler beim Anlegen eines Sockets");
    /* Erzeuge die Socketadresse des Servers
     * Sie besteht aus Typ, IP-Adresse und Portnummer */
    memset( &server, 0, sizeof (server));
    if ((addr = inet_addr( argv[1])) != INADDR_NONE) {
        /* argv[1] ist eine numerische IP-Adresse */
        memcpy( (char *)&server.sin_addr, &addr, sizeof(addr));
    }
    else {
        /* Für den Fall der Fälle: Wandle den
         * Servernamen bspw. "localhost" in eine IP-Adresse um */
        host_info = gethostbyname(argv[1]);
        if (NULL == host_info)
            error_exit("Unbekannter Server");
        /* Server-IP-Adresse */
        memcpy( (char *)&server.sin_addr,
                host_info->h_addr, host_info->h_length );
    }
    /* IPv4-Verbindung */
    server.sin_family = AF_INET;
    /* Portnummer */
    server.sin_port = htons( PORT );
    /* Baue die Verbindung zum Server auf */
    if(connect(sock,(struct sockaddr*)&server,sizeof(server)) <0)
        error_exit("Kann keine Verbindung zum "
                   "Server herstellen");
    /* Zweites Argument wird als "echo" beim Server verwendet */
    echo_string = argv[2];
    /* Länge der Eingabe */
    echo_len = strlen(echo_string);
    /* Denn String inkl. Nullterminator an den Server senden */
    if (send(sock, echo_string, echo_len, 0) != echo_len)
        error_exit("send() hat eine unterschiedliche Anzahl"
                   " von Bytes versendet, als erwartet !!!!");
    /* Schließe Verbindung und Socket */
#ifdef _WIN32
    closesocket(sock);
    /* Cleanup Winsock */
    WSACleanup();
#else
   close(sock);
#endif
    return EXIT_SUCCESS;
}

Galileo Computing - Zum Seitenanfang

28.6.2 Der Server  toptop

Und hier der Quellcode zur Server-Anwendung:

/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#ifdef _WIN32
/* Headerfiles für Windows */
#include <winsock.h>
#include <io.h>
#else
/* Headerfiles für UNIX/Linux */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
/* Portnummer */
#define PORT 1234
/* Puffer für eingehende Nachrichten */
#define RCVBUFSIZE 1024
#ifdef _WIN32
   static void echo(SOCKET);
#else
   static void echo( int );
#endif
static void error_exit(char *errorMessage);
/* Funktion gibt Daten vom Client auf dem stdout aus,
 * welche dieser mit der Kommandozeile übergibt */
#ifdef _WIN32
static void echo(SOCKET client_socket)
#else
static void echo(int client_socket)
#endif
{
    char echo_buffer[RCVBUFSIZE];
    int recv_size;
    time_t zeit;
    if((recv_size =
            recv(client_socket, echo_buffer, RCVBUFSIZE,0)) < 0)
        error_exit("Fehler bei recv()");
    echo_buffer[recv_size] = '\0';
    time(&zeit);
    printf("Nachrichten vom Client : %s \t%s",
            echo_buffer, ctime(&zeit));
}
/* Funktion gibt aufgetretenen Fehler aus und
 * beendet die Anwendung */
static void error_exit(char *error_message) {
#ifdef _WIN32
    fprintf(stderr,"%s: %d\n", error_message, WSAGetLastError());
#else
    fprintf(stderr, "%s: %s\n", error_message, strerror(errno));
#endif
    exit(EXIT_FAILURE);
}
int main( int argc, char **argv) {
    struct sockaddr_in server, client;
#ifdef _WIN32
    SOCKET sock, fd;
#else
    int sock, fd;
#endif
    int len;
#ifdef _WIN32
    /* Initialisiere TCP für Windows ("winsock") */
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD (1, 1);
    if (WSAStartup (wVersionRequested, &wsaData) != 0)
        error_exit( "Fehler beim Initialisieren von Winsock");
    else
        printf("Winsock initialisiert\n");
#endif
    /* Erzeuge das Socket */
    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0)
        error_exit("Fehler beim Anlegen eines Socket");
    /* Erzeuge die Socketadresse des Servers */
    memset( &server, 0, sizeof (server));
    /* IPv4-Verbindung */
    server.sin_family = AF_INET;
    /* INADDR_ANY Jede IP-Adresse annehmen */
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    /* Portnummer */
    server.sin_port = htons(PORT);
    /* Erzeuge die Bindung an die Serveradresse
     * (genauer an einen bestimmten Port) */
    if(bind(sock,(struct sockaddr*)&server, sizeof( server)) < 0)
        error_exit("Kann das Socket nicht \"binden\"");
    /* Teile dem Socket mit, dass Verbindungswünsche
     * von Clients entgegengenommen werden */
    if(listen(sock, 5) == –1 )
         error_exit("Fehler bei listen");
    printf("Server bereit – wartet auf Anfragen ...\n");
    /* Bearbeite die Verbindungswünsche von Clients
     * in einer Endlosschleife
     * Der Aufruf von accept() blockiert solange,
     * bis ein Client Verbindung aufnimmt */
    for (;;) {
        len = sizeof(client);
        fd = accept(sock, (struct sockaddr*)&client, &len);
        if (fd < 0)
            error_exit("Fehler bei accept");
        printf("Bearbeite Client mit der Adresse: %s\n",
           inet_ntoa(client.sin_addr));
        /* Daten vom Client auf dem Bildschirm ausgeben */
        echo( fd );
        /* Schließe die Verbindung */
#ifdef _WIN32
        closesocket(fd);
#else
        close(fd);
#endif
    }
    return EXIT_SUCCESS;
}

Generell werden die meisten Leser dieses Beispiel am lokalen Rechner testen. Also die Server- als auch die Clientanwendung befinden sich dabei auf einem Rechner. Wie schon erwähnt, ist dies dank des Loopback-Interfaces kein Problem. Die lokale IP-Adresse, die Sie auf Ihrem Rechner verwenden können, lautet hierbei 127.0.0.1 bzw. »localhost«. Wenn Sie allerdings die Möglichkeit haben, die Serveranwendung auf einem anderen System zu kompilieren und zu testen, so sollten Sie sich nicht scheuen, dies zu tun.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Das Programm bei der Ausführung:

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 28.4   Ein Client schickt einen String an den Server

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 28.5   Der Server bearbeitet die Anfragen des/der Clients

Natürlich stellt dieses Beispiel die primitivste Form der Netzwerkprogrammierung dar. Dennoch werden eigentlich alle wichtigen Funktionen der Netzwerkprogrammierung dazu verwendet.

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