Galileo Computing < openbook >
Galileo Computing - Bücher zur Programmierung und Softwareentwicklung
Galileo Computing - Bücher zur Programmierung und Softwareentwicklung


'Wie werde ich Unix-Guru' als Buch bestellen
A. Willemer
Wie werde ich UNIX-Guru
I  ANWENDUNG
Know-How für Unix/Linux-User: Einführung, Shell, Befehle, Hilfe, Arbeit mit Dateien, Editoren, Reguläre Ausdrücke, nützliche Tools, Hardware.

II  ADMINISTRATION
Tools, Systemstart, Benutzer verwalten, Hardware konfigurieren, Software installieren, Datensicherung, Tuning, Kernel

III  NETZWERK
Client/Server Systeme, TCP/IP, Routing, IPv6, Internet-Dienste, DHCP, Webserver, Firewalls

IV  DAS X-WINDOW SYSTEM
Die grafische Oberfläche von UNIX einrichten und nutzen

V  PROGRAMMIERUNG VON SHELLSKRIPTEN
Automatisieren von Tasks durch Shell-Skripte.

VI  PERL
Interpreter, Syntax, Variablen, Steuerung, Funktionen, UNIX-Aufrufe, GUIs mit Tk

VII  PROGRAMMIERWERKZEUGE
C-Compiler, Analyse-Tools, CVS, yacc, diff

VIII  UNIX-SYSTEMAUFRUFE
UNIX-Befehle in eigenen Programmen nutzen

IX  LITERATUR
Weiterführende Literatur zu UNIX und LINUX

 
Galileo Computing / <openbook> / "Wie werde ich UNIX-Guru ?"
« Zahlendreher ntoh und hton Client-Server Socketprogrammierung Mehrere Sockets parallel abfragen »

Unterabschnitte
  • Parallelität

Rahmenprogramm eines Client-Server Paars

Ein Server beantwortet in einer Endlosschleife Clientanfragen. Bevor er in diese Endlosschleife geht, muss er seinen Dienst anmelden. Er blockiert erstmals bei accept(), der durch den connect()-Aufruf des Clients freigegeben wird. Der anschließende Aufruf von recv() führt zwar auch zum Blockieren des Servers, aber da der Client sofort seine Anfrage senden wird, ist dies nur von kurzer Dauer. Er sendet die Antwort und wendet sich dem nächsten Anfrager zu.

[tcp-Server]
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>

#define MAXPUF 1023

main()
{
int IDMySocket, IDPartnerSocket;
struct sockaddr_in AdrMySock, AdrPartnerSocket;
struct servent *Service;
int AdrLen;

char Puffer[MAXPUF];
int MsgLen;

IDMySocket = socket(AF_INET, SOCK_STREAM, 0);
  /* Socket an Port-Nummer binden */
  AdrMySock.sin_family = AF_INET;
  AdrMySock.sin_addr.s_addr = INADDR_ANY; /* akzept. jeden */

/* Bestimme Port */
  Service = getservbyname("hilfe","tcp");
  AdrMySock.sin_port = Service->s_port;

bind(IDMySocket, &AdrMySock, sizeof(AdrMySock));
  listen(IDMySocket, 5);
  do {
    IDPartnerSocket = accept(IDMySocket, 
                             &AdrPartnerSocket, &AdrLen);
    MsgLen = recv(IDPartnerSocket, Puffer, MAXPUF, 0);

/* tu was mit den Daten */

send(IDPartnerSocket, Puffer, MsgLen, 0);
    close(IDPartnerSocket);
  } while(1); /* bis zum St. Nimmerlein */
}

Dieser Server bearbeitet nacheinander jede Anfrage, die über den Port »hilfe« an ihn gestellt wird. Nach jeder Anfrage wird die Verbindung wieder gelöst und ein anderer Client kann anfragen. Ein solcher Server dürfte auf jedem Betriebssystem arbeiten, das TCP/IP unterstützt. Lediglich der Aufruf von close() muss angepasst werden.

Der zugehörige Client bereitet die Verbindung in der Variablen AdrSocket vor und ruft damit die Funktion connect() auf. Diese blockiert bis der Server auf der anderen Seite accept() aufgerufen hat. Der Client fährt fort, indem er seine Anfrage sendet. Der Sendevorgang blockiert nie, dafür aber der anschließende Empfang der Antwort. Sobald der Server seine Antwort gesendet hat, kann der Client sich beenden.

[tcp-Client]
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>

#define MAXPUF 1023

main()
{
int IDSocket;
struct sockaddr_in AdrSock;
int len; /* Die Laenge der Socketstruktur */

struct hostent *RechnerID;
struct servent *Service;
char Puffer[MAXPUF];

IDSocket = socket(AF_INET, SOCK_STREAM, 0);

/* Bestimme den Zielrechner */
  RechnerID = gethostbyname("server");
  bcopy(RechnerID->h_addr,
        &AdrSock.sin_addr, RechnerID->h_length);

/* Bestimme den Port */
  Service = getservbyname("hilfe","tcp");
  AdrSock.sin_port = Service->s_port;

connect(IDSocket, (struct sockaddr *)&AdrSock,
          sizeof(AdrSock));

send(IDSocket, Puffer, MAXPUF, 0);
  recv(IDSocket, Puffer, MAXPUF, 0);
  close(IDSocket);
}

Es gibt zwei Variablen pro Socket. Die eine ist wie bei Dateizugriffen ein einfaches Handle (hier mit ID gekennzeichnet), die andere enthält die Adresse der Verbindung, also die Internet-Nummer des Rechners und die Nummer des Ports. Der Server legt die IP-Nummer des Rechners nicht fest, von dem er Anfragen akzeptiert. Das erreicht er, indem er die Konstante INADDR_ANY benutzt wird. Der Client dagegen gibt die Adresse des anzusprechenden Servers an. Die Funktion recv() liefert als Rückgabewert die Größe des versendeten Speicherbereichs. Die Funktion recv() liest die Sendung in Paketen von maximal 1KB. Wurden größere Pakete verschickt, müssen sie häppchenweise gelesen werden. Das Senden ist nicht beschränkt.

Parallelität

Der Server wird nun ergänzt, damit er die Vorteile einer Multitaskingumgebung nutzen und mehrere Anfragen parallel abarbeiten kann. Dazu muss an passender Stelle ein fork() eingebaut werden:

[Multitasking Server]
  do {
    IDPartnerSocket = accept(IDMySocket,
                             &AdrPartnerSocket, &len);
    if (fork()==0) {
      MsgLen = recv(IDPartnerSocket, Puffer, MAXPUF, 0);

/* tu was mit den Daten */

send(IDPartnerSocket, Puffer, MsgLen, 0);
      close(IDPartnerSocket);
      /* Sohn toetet sich selbst */
      exit(0);
    } /* if fork.. */
    close(IDPartnerSocket); /* der Vater schliesst Verbindung */
  } while(1);

Man sieht, mit welch geringer Änderung ein multitaskingfähiger Server zu realisieren ist. Beim Aufruf von fork() wird von dem Prozess ein Kindprozess erzeugt, der alle Ressourcen des Vaters besitzt. So kann er die Verbindung mit dem Anfrager weiter bearbeiten. Er tritt vollkommen an die Stelle des Vaters, der seinerseits die Verbindung schließen kann und auf eine neue Anfrage wartet. Sobald diese eintrifft, wird wieder ein Kind generiert, der gegebenenfalls parallel zum anderen Kind arbeitet, falls jenes noch nicht fertig ist.

Der Server ist so, wie er nun vorliegt, ein statusloser Server (stateless server). Das bedeutet, er kann sich den Stand einer Kommunikation nicht merken. Fragt derselbe Client noch einmal an, wird er ihn wie eine völlig neue Anfrage behandeln. In dieser Art arbeitet ein Webserver. Jede Anfrage ist für ihn neu. Andere Server, beispielsweise POP3-Server, halten die Verbindung mit ihrem Client solange aufrecht, bis beide ein Ende der Verbindung vereinbaren. In solch einem Fall würde eine Schleife im Sohnprozess über recv(), Verarbeitung und send() laufen, bis ein definiertes Ende der Kommunikation stattfindet. Natürlich würde dann auch der Client eine Schleife haben, die erst bei Einigung über den Verbindungsabbau beendet wird.

Wie im Zusammenhang mit Signalen gezeigt, sollte die Entstehung von Zombies verhindert werden. Zombies entstehen, wenn ein Sohn endet, aber der Vater nicht auf ihn wartet. Dadurch bleibt ein Eintrag in der Prozesstabelle mit dem Exitwert des Sohnes. Der Aufwand ist denkbar gering:

signal(SIGCLD, SIG_IGN);

Diese Zeile sollte dem Server eingebaut werden, bevor er in die Endlosschleife läuft.



« Zahlendreher ntoh und hton | Client-Server Socketprogrammierung | Mehrere Sockets parallel abfragen »
 
 Zum Katalog
Zum Katalog
Wie werde ich UNIX-Guru?
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 UNIX/Linux

PHP 4-Workshop

Einstieg in Python

Perl fürs Web

MySQL 4

GNOME 2.0
 Empfehlung

Einstieg in XML
 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
Info

 MyGalileo
Der Service für registrierte Leser:
Zu MyGalileo
Info



Copyright © Galileo Press GmbH 2003
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 GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de