[Tutorial] Ein Chat Server in Java – Teil 1 / 2

In diesem Tutorial wollen wir einen kleinen Chat Server in Java mit der Library vertx.io schreiben. Ursprünglich wollte ich lediglich einen kleinen Prototyp schreiben, um vertx.io etwas genauer kennen zu lernen, aber dann habe ich gleich angefangen ein kleines Tutorial dazu zu veröffentlichen, um diese Erfahrung mit euch zu teilen.

Der Chat Server & Client wird nur in der Console laufen, allerdings ist es ein leichtes, eine GUI für ihn zu bauen, da diese bereits beim Entwurf mit berücksichtigt wurde. Zu Beginn soll ein Benutzername eingegeben werden, danach kann der User direkt Nachrichten eingeben und mit ENTER zum Server schicken, um mit den anderen Nutzern zu chatten. Das ganze wird als JSON String übertragen. Die Implementierung des Clients folgt im 2. Teil des Tutorials!

Der Chat Server ist übrigens nicht auf eine Anzahl an Clients begrenzt, er kann so viele Chat Clients aufnehmen, bis der RAM voll oder die CPU ausgelastet ist. 😀

Chatserver Architektur

 

Chat Server Screenshot:

Chat Server 1.0.0 Log

Hinweis:

Das Einbinden der Library mit Maven werde ich hier nicht behandeln, da dies den Rahmen sprengen würde.
Den kompletten Source Code findet ihr hier: https://github.com/JuKu/chat-server-tutorial

 

#1 Der Chat Server

Zuerst kümmern wir uns um den Chat Server. Bei Vertx benötigt man zu allererst, sowohl beim Server, als auch beim Client, eine Vertx Instanz.

 

Als nächstes wollen wir einen TCP Server mit Vertx.io erstellen:

 

Jetzt haben wir den TCP Server bereits erstellt, allerdings läuft er noch nicht. Bevor den Server starten können, müssen wir noch einen Connection Handler hinzufügen, der aufgerufen wird, wenn eine neue Verbindung zum Server hergestellt wurde.

 

Nun können wir den Server endlich starten:

 

Schließen können wir den Server wie folgt:

 

Damit wir die Clients einfacher händeln können, habe ich eine Klasse ChatClient (auf Serverseite) geschrieben:

 

Da ich in dieser Klasse schon einige weitere Features wie die Authorisierung hinzugefügt habe (noch nicht vollständig, einen richtigen Login mit Username & Passwort müsst ihr schon selbst implementieren), ist sie etwas größer geworden.

 

Gehen wir sie einmal Stück für Stück durch.

Im Konstruktor übergeben wir den Socket zum Client und einen MessageListener, um später die empfangenen Nachrichten an eine zentrale Stelle weitergeben und broadcasten (an alle Clients verteilen) zu können. Diese Klasse ist ja jeweils für einen einzelnen Client zuständig. Als nächstes erstellen wir eine eindeutige clientID (long), einfach damit der Server die Nachrichten & Clients besser handeln kann.

Danach wird es interessanter, denn mit socket.handler() registrieren wir den Nachrichten Handler, also die Methode, die aufgerufen wird, wenn eine neue Nachricht vom Client empfangen wurde. Dabei arbeiten wir die Nachricht aber nicht in der selben Methode, sondern geben diese an die Methode messageReceived() weiter. Die Nachricht ist in diesem Fall noch ein Vertx Buffer, also einfach nur eine Art Byte Buffer.

Nachdem wir den Handler registriert haben, schicken wir noch eine Willkommens-Nachricht zum Client, um ihm zu sagen, dass er sich einloggen soll.

 

In dieser Methode erstellen wir eine neue Chat Nachricht (die Klasse ChatMessage folgt später), mit der clientID 0 (clientID und username sind eig. unnötig, ich habe sie dennoch eingebaut, damit das Protokoll einheitlich ist und einem Standard folgt). Der Text danach ist optional.

Da wir Strings versenden wollen (wir könnten aber genauso reine Bytes mit Vertx verschicken, würde dieses Beispiel aber komplizierter machen) konvertieren wir die Chat Nachricht zuerst zu einem JSON String. Dieser sieht dann in etwa so aus:

Da es die erste Nachricht ist, die wir vom Server zum Client schicken, senden wir hier noch die Server Version mit, damit der Client prüfen kann, ob er überhaupt kompatibel mit dem Server ist. Evtl. kann man auch noch den Servernamen mitschicken usw. Mit dem Action request_auth fordern wir den Username vom Client an und schicken die ganze Nachricht dann mit send() zum Client. Diese Methode wandeln das JSON Objekt in einen String um und schickt das ganze mittels socket.write(str) zum Client.

 

Nun zur komplexesten Methode (gekürzt) in dieser Klasse:

In dieser Methode wird die empfangene Nachricht zuerst in einen String und dann in ein JSON Objekt umgewandelt. Danach wird geprüft, ob es sich um eine Login Nachricht handelt und wenn ja der Benutzername übernommen und der Status auf eingeloggt gesetzt. Dann wird dem Client noch mitgeteilt, dass er jetzt eingeloggt ist.

Wenn es sich um eine normale Nachricht handelt, wird weiter unten geprüft, ob der Nutzer überhaupt schon eingeloggt ist, da nur eingeloggte Nutzer Nachrichten schreiben können. Ansonsten wird die Nachricht einfach mit einer Errormeldung verworfen. Zu guter letzt wird die Nachricht dem Message Listener / Message Receiver übergeben, wozu wir aber später noch kommen.

 

Die Klasse ChatMessage sieht wie folgt aus:

Einige Methoden habe ich der Übersichtlichkeit halber rausgekürzt, im Source Code sind sie aber alle vollständig enthalten.

Eine Chat Nachricht besteht also aus den folgenden Attributen:

  • der ClientID, damit der Client z.B. (wenn ihr ihn um eine GUI erweitert) jedem User eine eigene Farbe geben kann
  • ein Username
  • der eig. Chat Text
  • Action (optional, wird aber für Login benötigt), z.B. für Logins oder wenn der Server dem Client später einmal Befehle schicken soll (falls ihr ihn erweitern wollt)
  • Result (optional, aktuell nur für Login verwendet)

 

Kommen wir noch zur Main Klasse:

 

Ich glaube diese Klasse ist relativ selbst erklärend. Wir erstellen erst eine neue Instanz des Chat Servers,  registrieren den Message Listener / Message Receiver, der alle Nachrichten von den Clients erhält und broadcasten in dieser die empfangene Nachricht dann an alle Clients, außer dem Sender (mit der clientID).

Danach wird der Server mittels server.start() gestartet. Als nächstes brauchen wir eine Map

damit wir die Nachrichten zu den einzelnen Clients weiterleiten können.

 

Nun zu der wichtigsten Klasse ChatServer, die den meisten Netzwerk & Vertx.IO Code enthält (gekürzt):

 

Auf 2 Methoden will ich hierbei nochmal etwas genauer eingehen, zuerst auf die Methode addClient():

Wenn ein neuer Client sich zum Server verbunden hat (im Connection Handler), wird diese Methode aufgerufen und eine neue ChatClient Instanz erstellt.

Außerdem wird ein Close Handler registriert, damit der Client wieder aus der Map entfernt wird, wenn die Verbindung geschlossen wurde. Zuletzt wird der Client der Map hinzugefügt.

 

Diese Methode schickt eine Chat Nachricht an alle registrierten Clients, sofern diese eingeloggt sind (Broadcast).

 

Das wars erstmal zum 1. Teil des Tutorials, im 2. Teil implementieren wir den Chat Client, der 2. Teil wird vermutlich sogar etwas kürzer.

Falls ihr Fragen oder Anregungen habt, könnt ihr diese gerne wieder in die Kommentare schreiben! 😀

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.