FAQ: HTML5-Echtzeitanwendungen mit Hilfe des WebSocket-Protokolls entwickeln

Wie erstelle ich eine HTML5-Echtzeitanwendung mit Hilfe des WebSocket-Protokolls? Die folgende Beschreibung geht davon aus, dass ein Java-Development-Kit (JDK), die Entwicklungsumgebung NetBeans und der Application-Server Tomcat in einer möglichst aktuellen Version unter Microsoft-Windows installiert sind.

  Als Beispiel erstellen wir hier einen sehr einfachen WebSocket-Server, der sich eine Zahl im Bereich von
  1 und 1000 ausdenkt, die zu erraten ist. Zu jeder Zahl, die von einem Client übermittelt wird, wird eine
  Information an alle beteiligten Clients ausgegeben, ob die Zahl zu gross oder zu klein ist. Wer die Zahl
  zuerst errät, ist Sieger. Der Server denkt sich dann eine neue Zahl aus und das Spiel beginnt erneut.

  Wer eine message an den Server übermittelt, die nicht in eine Zahl verwandelt werden kann, der wird
  disqualifiziert, d.h. der Server beendet die Session.

  Für die Implementierung unseres WebSocket-Servers legen wir in NetBeans ein neues Projekt wie folgt an:

  1. File / New Project
  2. Unter Categories "Java Web" und "Web Application" wählen
  3. Dann "Next" anklicken
  4. Project Name: Spiele
  5. Dann "Next" anklicken
  6. Server: Apache Tomcat or TomEE
  7. Context Path: Spiele
  8. Dann "Next" anklicken
  9. Dann "Finish" anklicken

  F6 oder Klick auf den grünen Startpfeil startet die Java Web Applikation im Browser (es wird
  der Standardbrowser des Systems gestartet) unter der URL http://localhost:8080/Spiele

  Die gestartete Java Web Applikation zeigt erst einmal nur den Inhalt "TODO write content" einer
  index.html Datei an, die bei Erstellen des Projektes automatisch erzeugt wurde. Diese Datei eignet
  sich gut, um das Projekt zu dokumentieren.

  Wir legen nun unseren Zahlenraten-WebSocket-Endpoint in unserem Spiel an (später können wir weitere
  Spiele ergänzen...):

  1. Rechtsklick auf den Folder "Source Packages"
  2. New / Java Package
  3. Package Name: spiele
  4. Dann "Finish" anklicken
  5. Rechtsklick auf den den neuen Folder "spiele"
  6. New / Other  (Hinweis: Other steht ganz unten)
  7. Unter Categories "Web" und unter File Types "WebSocket Endpoint" wählen
  8. Class Name: ZahlenratenEndpoint
  9. WebSocket URI: /zahlenraten
 10. Dann "Finish" anklicken

 Es entsteht die folgende Klasse ZahlenratenEndpoint:

 

 Die Klasse ZahlenratenEndpoint repräsentiert unseren WebSocket-Server, ist also
 die Zentrale unseres kleinen Spiels. Eine Methode onMessage wurde bereits
 angelegt. Diese Methode wird immer dann automatisch aufgerufen, wenn ein Client
 eine Nachricht an diesen Server sendet.

 Einige Methoden, die wir unbedingt benötigen, und die unser ServerEndpoint auch
 besitzt, fehlen aber noch: onOpen, onClose und onError.

 Diese drei fehlenden Methoden können wir generieren lassen, indem wir in Zeile 16
 links neben 'public class ZahlenratenEndpoint' auf das Birnchen klicken und die
 drei Methoden einzeln hinzufügen. Danach sieht unser Quellcode wie folgt aus:

 

 Damit wir wissen, wer die Verbindung öffnet oder schliesst, benötigen wir die
 erweiterten Varianten von onOpen und onClose, die ein Session-Objekt übergeben.
 Dasselbe gilt für die Methoden onMessage und onError, denn auch hier wüssten
 wir gerne, wer (also welcher Client) eine Nachricht oder einen Fehler sendet.
 Wir ergänzen deshalb bei jeder der vier Methoden ein Session-Objekt als ersten
 Parameter. Danach sieht unser Quellcode wie folgt aus:

 

 Die Sessions der Clients müssen auf dem Server verwaltet werden. Jede Session
 besitzt eine eindeutige id, die über session.getId() abgefragt werden kann.

 Zur Verwaltung der Sessions legen wir eine HashMap SESSIONS an.
 Die keys in der HashMap SESSIONS sind die id's der Sessions,
 die values der HashMap SESSIONS sind die Session-Objekte.

 Wird eine neue Session geöffnet (vom Client), dann wird automatisch die
 Methode onOpen(Session session) auf dem Server aufgerufen. Die übergebene
 Session wird dann in der HashMap SESSIONS eingefügt. Dem neuen Client wird ein
 Begrüssungstext gesendet, das geht über session.getBasicRemote().sendText("Hallo");

 Hier könnte natürlich allen anderen bereits vorliegenden Clients über ihre
 Session mitgeteilt werden, dass sich ein neuer Client eingefunden hat. Dafür
 müsste einfach allen sessions, die in der HashMap SESSIONS vermerkt sind, eine
 Nachricht gesendet werden. Wir unterlassen das hier, um den Spielfluss der
 bereits angemeldeten Clients nicht zu stören :)

 Wird eine Session vom Client geschlossen, dann wird automatisch die
 Methode onClose(Session session) auf dem Server aufgerufen. Die übergebene
 Session wird dann wieder aus der HashMap SESSIONS entfernt.

 Danach sieht unser Quellcode wie folgt aus:

 

 Für die zu erratende Zahl ergänzen wir eine statische Variable numberToGuess und
 initialisieren Sie mit einer zufälligen Zahl aus dem Bereich von 1 bis 1000:

 

 Wenn ein Client Nachrichten zum WebSocket-Server überträgt, wird auf dem Server
 die Methode onMessage(Session session, String message) aufgerufen. In dieser
 Methode werden also die Zahlen, die der Client übermittelt, entgegen genommen.
 Hier ist nun Einiges zu tun. Die Zahl, die im String message steckt, muss erst
 einmal geparst werden. Je nachdem, ob die Zahl ungültig, gültig, zu groß, zu
 klein oder passend ist, muss verschieden reagiert werden:

 

 Jetzt sind noch die rot unterstrichenen Methoden disqualify, sendTextForAll
 und sendTextForAllExcept passend zu implementieren:

 

 Damit ist unser einfacher WebSocket-Server zum Zahlenraten fertig. Erreichbar ist
 unser WebSocket-Endpoint über die Adresse: ws://localhost:8080/Spiele/zahlenraten
 (localhost:8080 ist entsprechend anzupassen, wenn der Server nicht lokal läuft)

 Download: Wer nicht tippen möchte, kann ZahlenratenEndpoint.java herunterladen.

 Es ist klar, dass dieser Server noch an vielen Stellen verbessert werden könnte.
 Aber er soll nur als Beispiel dienen. Wer diese Technik verstanden hat, kann sie
 nutzen, um beliebige HTML5-Echtzeitanwendungen zu entwickeln. Dazu gehören auch
 Spiele mit grafischer Oberfläche, Kollaborations-Applikationen und Chat-Systeme.

 Das WebSocket-Protokoll stellt einen allgemein akzeptierten Standard dar, der
 inzwischen von allen gängigen Browsern und Programmiersprachen in Form von API's
 unterstützt wird.

 Nun ist es an der Zeit, einen WebSocket-Client für unser Zahlenrätsel zu erstellen.
 Wir erstellen uns hier einen WebSocket-Client für unser Spiel und
 starten mit der Html-Datei ZahlenratenClient.html.

 Das geht mit einem einfachen Editor wie Notepad++ oder Atom:

 

 Im Eingabebereich gibt es ein Eingabefeld für eine Zahl und einen Button, der
 die Funktion zum Versenden der Zahl an den WebSocket-Server bei Click ausführt.
 Der Typ des Eingabefeldes ist "text", hier wäre natürlich "number" geeigneter.
 Aber für Testzwecke nehmen wir hier "text", damit auch die Disqualifikation
 getestet werden kann.

 Der Ausgabebereich ist für die Nachrichten vorgesehen, die vom WebSocket-Server
 hereinkommen.

 Auf Gestaltung über CSS verzichten wir, da es hier um die reine Dokumentation
 der Erstellung eines WebSocket-Clients geht.

 Das Herzstück unseres WebSocket-Clients steckt in der eingebunden JavaScript
 Datei ZahlenratenClient.js. Dort wird die Verbindung aufgebaut und verwaltet:

 

 Damit haben wir alles zusammen und wir können unser Spiel testen und spielen.
 WebSocket-Server starten und die Datei ZahlenratenClient.html in einem Browser
 aufrufen:

 

 Obige Ansicht zeigt das Spiel im Browser, wenn es einmal durchgespielt wurde.

 Download: Wer nicht tippen möchte, kann sich die Dateien ZahlenratenClient.html
 und ZahlenratenClient.js hier herunterladen.

Zusammenfassung: WebSocket-Server und WebSocket-Clients Obiges Beispiel eignet sich als Vorlage für viele Echtzeitanwendungen, die auf dem WebSocket-Protokoll basieren. WebSocket-Server und WebSocket-Clients lösen jeweils auf der Gegenseite Events aus (open, close, message und error). An diese Events lassen sich über Listener Funktionen hängen, die dann entsprechend reagieren. All das zeigt das obige Beispiel. Der WebSocket-Server ist die Zentrale, über die die gesamte Kommunikation abläuft. Wenn ein Client eine Nachricht an den Server sendet, verteilt dieser diese Nachricht an die anderen Clients weiter (soweit erforderlich und gewünscht). Auch das wird im obigen Beispiel gezeigt. Wer dieses Beispiel versteht und anwenden kann, sollte auch kompliziertere Echtzeitanwendungen erstellen können:

- Echtzeitanbindungen an Datenbanken,
- Grafisch aufbereitete Spiele, die man gemeinsam spielt, und die in Echtzeit reagieren (z.B. auf Maus-Events)
- Kollaborationstools (z.B. elektronische Schultafeln, auf der alle gemeinsam arbeiten können)
- Chat-Systeme
- u.v.m. ...

Hinweis: Server und Clients werden häufig von verschiedenen Parteien erstellt. Damit das funktioniert, muss abgesprochen werden, wie welche Daten ausgetauscht werden. Es ist also die Absprache eines Protokolls nötig. Häufig gibt der Server das Protokoll vor und es wird eine schriftliche Dokumentation bereitgestellt. Im obigen Beispiel werden einfach gegenseitig Textnachrichten versendet. Soll reichhaltigere Information ausgetauscht werden, dann könnten die Nachrichten im JSON-Format ausgetauscht werden. Wenn Server und Clients gleichzeitig erstellt werden, sollten beide Parteien vorab besprechen, welche Nachrichten von der Gegenseite gesendet werden und wie darauf reagiert werden muss.

Schreiben Sie doch mal einen WebSocket-Client, der dem obigen Client beim Zahlenraten überlegen ist! Eine Instanz des WebSocket-Servers zum Zahlenraten läuft im Internet und ist unter der Adresse 'ws://mist.lernen42.de:8080/Spiele/zahlenraten' erreichbar. Auch eine Version vom obigen WebSocket-Client, der sich mit diesem Server verbindet, ist verfügbar: http://mist.lernen42.de/spiele/ZahlenratenClient.html. Schaffen Sie es, einen WebSocket-Client zu programmieren, der Ihnen beim Zahlenraten Vorteile gegenüber dem obigen Client verschafft?

Oder verbessern Sie den WebSocket-Server! Es wäre doch schön, wenn die Clients wüssten, wie viele andere Teilnehmer es am Spiel gerade gibt. Oder sogar, wer die anderen Teilnehmer sind. Oder wie viel Prozent ihrer Spiele Sie und die anderen Teilnehmer gewinnen. Es gibt viel zu tun...

Wie kann ich meinen WebSocket-Service auf einem (fremden) Tomcat-Server bereitstellen? Während der Programmentwicklung kümmert sich die NetBeans-Entwicklungsumgebung darum, den WebSocket-Service über den Tomcat-Application-Server bereitzustellen. Möchte man den fertigen WebSocket-Service nun über ein anderen Application-Server verfügbar machen, so ist das recht einfach. Eine lauffähige Web-Applikation besteht aus einer Datei mit der Endung .war, die NetBeans im Unterordner dist des Projektes ablegt. Der WebSocket-Service steckt beispielsweise in der Datei ChatServer.war. Im Verzeichnisbaum eines Tomcat-Application-Servers gibt es einen Unterordner webapps. Dorthin können fertige Applikationen wie ChatServer.war kopiert werden. Der Tomcat-Server startet diese dann automatisch und der WebSocket-Service steht bereit. Zum Löschen eines WebSocket-Services muss lediglich die entsprechende war-Datei aus dem Ordner webapps entfernt werden.