JavaScript/WebSocket/Einführung
- 10min
- mittel
- JavaScript
WebSockets sind eine Erweiterung des HTTP Protokolls. Eine normale HTTP Verbindung besteht aus einem einzelnen Sendevorgang, gefolgt von einer einzelnen Antwort. Danach wird die Verbindung wieder geschlossen. Eine WebSocket-Verbindung beginnt ihr Leben als normale HTTP Verbindung, die dann mit Hilfe des Upgrade-Mechanismus von HTTP 1.1 zu einer Websocket-Verbindung hochgestuft wird. Die Folge davon ist, dass die Verbindung erst geschlossen wird, wenn es explizit angefordert wird. Solange die Verbindung offen ist, können beide Parteien nach Belieben Daten senden.
Inhaltsverzeichnis
Der Vorteil gegenüber dem HTTP-Protokoll ist, dass der Client (also z.B. der Browser) Daten vom Server bekommen kann, ohne sie explizit abholen zu müssen. Damit entfällt das Senden der Anfrage durch den Client, der Verbindungsaufbau und die Verarbeitung und Übertragung der HTTP-Header, was Zeit spart und auch die übertragene Datenmenge reduziert.
Hinzu kommt, dass ein HTTP-Client, der ohne Websockets zeitnah Serverdaten erhalten will, relativ oft beim Server anfragen muss. Eine der üblichen Techniken aus der Zeit vor WebSockets war das long polling, bei dem der Client eine Datenabfrage gestartet hat und diese Abfrage auf dem Server durch ein Programm bedient wurde (beispielsweise in PHP), das nach Daten für diesen Client sucht und sie sendet, wenn sie vorhanden sind. Liegen keine Daten vor, wartet das Serverprogramm einen Moment (z.B. 500ms) und schaut erneut. Nach einer gewissen Anzahl von Fehlversuchen bricht es ab, damit es zu keinem Verbindungs-Timeout kommt, und der Client muss eine neue Anfrage senden. Dieses Verfahren reduziert immerhin die Menge an erforderlichen HTTP Requests, aber führt dazu, dass auf dem Webserver für jeden Client ein langlaufender Request aktiv ist, der ständig nach Daten fragt, wodurch der Webserver stark belastet wird.
Webserver sind auf einen schnellen Request-Response Mechanismus hin optimiert und nicht darauf ausgelegt, Verbindungen dauerhaft aufrecht zu erhalten oder sehr viele Requests parallel zu verarbeiten. Hinzu kommt, dass die Verbindung nicht alles ist - es muss ja auch auf dem Server ständig ein Programm aktiv sein, das sich als Socket-Listener registriert hat, die einlaufenden Daten verarbeitet und Antworten sendet. Obwohl ein HTTP-Webserver relativ problemlos tausende von HTTP-Anwendern bedienen kann (sofern sie nicht zu viele Requests auf einmal senden), wird er durch viele long polling Anfragen schnell überlastet.
Ein auf WebSockets spezialisierter Server benötigt keine polling-Schleifen. Statt dessen werden die Daten, die für einen Client bestimmt sind, einfach in die für ihn offengehaltene WebSocket-Verbindung geschrieben. Trotzdem ist ein WebSocket-Server nicht für so viele Clients geeignet wie ein WebServer, was Sie bei Ihrer Hardwareauswahl berücksichtigen müssen (Sie werden nicht vermeiden können, die genauen Zahlen durch eigene Lasttests zu ermitteln).
Es ist üblich, den Websocket-Server allein für das Websocket-Protokoll zu nutzen und die Behandlung normaler HTTP Anfragen einem klassischen Webserver zu überlassen. Um für die Clients eine einheitliche Schnittstelle anzubieten, wird dafür auf dem Webserver ein so genannter Reverse Proxy eingerichtet. Der Webserver - beispielsweise Apache ab Version 2.4.5 oder nginx ab Version 1.3.13 - verarbeitet eine ankommende WebSocket-Verbindung dann nicht selbst, sondern leitet sie an einen anderen Serverprozess weiter, der für WebSocket-Verarbeitung geeignet ist. Dieser zweite Prozess kann auf dem gleichen Server liegen oder auf einem anderen Server. Der als Reverse-Proxy agierende Webserver kann auch eine Lastverteilung durchführen, die ankommenden WebSocket-Verbindungen also auf mehrere Websocket-Server aufteilen.
Der WebSocket-Server kann auf unterschiedliche Arten realisiert werden.
- node.js
- Ein node.js Server kann sowohl als Webserver als auch als WebSocket-Server dienen. Es gibt mehrere node.js Libraries, die beim Umgang mit WebSockets helfen, z. B. ws oder socket.io.
- C++/Java/C#/...
- Sie können ein Programm in einer Sprache Ihrer Wahl erstellen, das als Dienst auf Ihrem Server läuft und Websocket-Anfragen auf einem Port Ihrer Wahl bedient. Die Erstellung eines solchen Dienstes sprengt den Rahmen des Selfhtml Wikis.
- PHP
- Sie können einen solchen Dienst ebenfalls in PHP erstellen. Dieses PHP Programm läuft dann aber nicht unter Kontrolle Ihres Webservers, sondern als eigenständiges Programm. Bibliotheken wie beispielsweise Ratchet helfen bei der Realisierung.
- IIS 8 mit ASP.NET 4.5
- Die Kombination von IIS Webserver mit ASP.NET 4.5 ermöglicht das Erstellen eines WebSocket-Servers direkt im Webserver.
WebSockets und HTTP/2
Es gibt Diskussionen, ob das in HTTP/2 vorgesehene PUSH-Verfahren ein Ersatz für WebSockets sein kann. Wir sind zu dem Ergebnis gekommen, dass die Mehrheit der gefundenen Aussagen für eine Koexistenz von WebSockets und HTTP/2 spricht. Es gibt Anwendungsfälle für beides, und es gibt WebSocket-Anwendungsfälle, die sich besser mit HTTP/2 lösen lassen. Das PUSH-Verfahren von HTTP/2 ist aber eher auf die optimierte Kommunikation von Server und Browser ausgelegt, nicht auf die Kommunikation mit einer JavaScript-Anwendung.
WebSockets und Server Sent Events
Mit SSEs (Server Sent Events) wird die Luft für WebSockets schon dünner. Es gibt etliche Stimmen, die das EventSource
Objekt für die bessere Lösung halten, um Daten ohne explizite Anfrage vom Server an den Client zu senden. Wenn der Client Daten zum Server schicken muss, empfehlen diese Quellen normale HTTP Requests. Bei SSE ist allerdings zu beachten, ob Browser und Server HTTP/2 unterstützen. Mit HTTP 1.1 ist die Anzahl der möglichen SSE Verbindungen von einem Browser (über alle Tabs hinweg) zu einer Domain auf 6 begrenzt. Unter HTTP/2 können Server und Client einen höheren Wert aushandeln.
Der Vorteil von SSE besteht definitiv in einer einfacheren Implementierung. WebSockets sind aufwändiger in der Umsetzung.
Ein Vorteil von WebSockets ist die bidirektionale Datenübertragung und die Möglichkeit, binäre Datenblöcke zu senden. SSE ist auf UTF-8 codierten Text beschränkt (was sich durch eine base-64 Codierung natürlich umgehen lässt, aber dann steigt das übertragene Datenvolumen).