BUGH Wuppertal Fachbereich 7 - Mathematik Axel Rogat
Betriebssysteme und betriebssystemnahes Programmieren
Übungsblatt 9

Aufgabe 19
Schreiben Sie in C++ Server und Client zu einem einfachen Internet-Chat.

  • Beliebig viele Benutzer auf verschiedenen Rechnern sollen sich über Ihren Server miteinander unterhalten können. Alle geben jeweils eine Zeile in ihrem Client ein, der sie unverändert an den Server schickt. Der Server wiederum stellt der Zeile das Pseudonym (den "Nickname") des Redenden voran und verschickt das Ergebnis an alle angeschlossenen Clients, also etwa "AXL: hallo!".

  • Der Server erhält als Aufrufparameter den zu verwendenden Port; der Client erhält den Rechnernamen des Servers, den Port und den zu verwendenden Nickname, z.B.:
    chatserver 4711 chatclient wman4 4711 AXL
  • Der Client soll einfach in einem normalen xterm-Fenster betrieben werden. Die unterste Zeile wird für die Eingabe verwendet, in den Zeilen darüber scrollen die ankommenden Nachrichten nach oben (s.u.).

  • Zwei spezielle Eingabezeilen sind in den Clients definiert. "/names" gibt eine Liste aller momentan angeschlossenen Teilnehmer aus, etwa im Stil "AXL von 132.195.7.49 (wman4.math.uni-wuppertal.de)". Die Zeile "/bye" beendet den Client (er soll auch sicher mit CTRL-C abgebrochen werden können).

  • Wenn ein neuer Teilnehmer angeschlossen wird, soll der Server die Nachricht "nickname kommt herein" an alle Clients schicken. Wenn sich ein Teilnehmer abkoppelt, soll "nickname verläßt uns" verschickt werden.

Hinweise:

Server: Es werden viele unterschiedliche Prozeßkommunikations-Mechanismen benötigt. Bitte gehen Sie genau so vor, wie hier beschrieben wird.

  • Für die Internet-Verbindungen arbeiten Sie wie üblich mit Streamsockets. Sie benötigen einen Listen-Socket und danach beliebig viele Accept-Sockets. Sie müssen auf alle Accept-Sockets horchen und gleichzeitig neue Verbindungen annehemen können! Spalten Sie daher für jeden neuen Teilnehmer nach dem accept mittels fork einen eigenen Prozeß ab, der ihn im weiteren bedient. (Bitte spalten Sie nicht nur einen Prozeß ab, der mittels select alle Accept-Sockets bedient.)

  • Die Teilnehmerliste soll wirklich beliebig groß werden können. Definieren Sie eine geeignete Struktur (struct chatter o.ä.), die mindestens Namen, Socket-Deskriptor, Adresse, PID des Kind-Prozesses und Platz für eine Nachricht (80 Zeichen) enthält. Die Teilnehmerliste ist dann ein Vektor mit diesem Grundtyp (C++-Klasse vector).

  • Der Elter-Prozeß und die Kind-Prozesse müssen Daten austauschen, was am besten über gemeinsamen Speicher geschieht. Da es schwierig wird, den variablen großen Vektor in einem solchen Segment unterzubringen, wird die Teilnehmerliste nur vom Elter geführt. Nur der Elter ist auch dafür zuständig und imstande, Nachrichten an Clients zu verschicken.

    Das gemeinsame Speicher-Segment ist nur genau einen Chatter groß. Es muß allerdings mit einem binären Semaphor vor Inkonsistenz geschützt werden, da Elter und alle Kinder darauf zugreifen müssen.

  • Als erstes empfängt jeder Kind-Prozeß vom Client den Nickname und muß ihn über das Segment dem Elter mitteilen. Dazu schickt er dem Elter ein SIGUSR1-Signal (vergleiche das Shared-Memory-Beispiel aus der Vorlesung). Der Elter nimmt im Signalhandler den Teilnehmer neu in die Teilnehmerliste auf und verschickt die "kommt herein"-Meldungen.

  • Wenn ein Kind später eine Nachricht vom Client erhält, trägt er sie in das Segment ein und schickt dem Elter ein SIGUSR2. Der Elter muß im Handler die Nachricht verbreiten.

  • Für die Semaphore verwenden Sie nicht die Klasse aus dem Skript, da der Umweg über das Lockfile hier unnötig ist. Verwenden Sie direkt die UNIX-Systemaufrufe oder die einfachen Routinen aus minisem.cpp (und minisem.h).

  • Wenn der Server mit CTRL-C abgebrochen wird, soll er geordnet alle Verbindungen schließen und Semaphor und Segment freigeben!

  • Die Client-Adresse können Sie wie der Operator aus der Vorlesung ausgeben. Den symbolischen Namen erhalten Sie mittels gethostbyaddr.

Client: Der Client ist relativ einfach:

  • Das erste, was der Client dem Server schickt, ist der Nickname des neuen Teilnehmers.

  • Danach soll sich auch der Client mit fork in zwei Prozesse aufspalten - einen zur Annahme der Eingaben des Benutzers, einen zum Empfangen der Nachrichten vom Server.

  • Wenn der Client mit CTRL-C beendet wird, soll er dem Server die Nachricht "/bye" schicken, damit dieser den Teilnehmer austragen und verabschieden kann.

Kleinere Probleme ergeben sich lediglich bei der Terminal-Steuerung. Zum ersten Testen geben Sie die Nachrichten einfach auf den Fehlerkanal aus und arbeiten mit Ihrem Skript log aus Aufgabe 15! Gehen Sie danach vor wie hier beschrieben:

  • Die Eingabe soll immer in der untersten Zeile erfolgen, die Ausgaben darüber. Sie können dazu einen beliebigen Terminal-Steuermechanismus verwenden (curses, ANSI-Steuersequenzen). Einfache Routinen mit xterm-Sequenzen liegen auf der Homepage bereit (xterm_seq.cpp und xterm_seq.h).

  • Beachten Sie dabei, daß Sie zwar zwei Prozesse, aber nur ein Terminal zur Verfügung haben (und insbesondere nur eine Cursor-Position). Wenn die Eingabe einer neuen Nachricht durch das Anzeigen von Server-Meldungen unterbrochen wird, muß die Eingabezeile danach restauriert werden.

    Gehen Sie wie folgt vor: Verwenden Sie einen binären Semaphor, um sicherzustellen, daß nur ein Prozeß gleichzeitig auf das Terminal schreibt. Wenn eine Server-Nachricht dargestellt wurde (die Eingabezeile wird zerstört), soll dieser Prozeß dem anderen ein SIGUSR1 schicken. Der andere stellt daraufhin die Eingabezeile neu dar. Damit das möglich ist, können Sie kein normales gets o.ä. verwenden, sondern müssen die Zeichen einzeln annehmen, Backspace behandeln, etc. Die Routine xterm_readline erledigt das für Sie.

Viel Erfolg!

 
email: axel@math.uni-wuppertal.de Zurück Abgabe: 2.7.1999
 

© 1999 Axel Rogat