Das Problem: Client-Server- und Server-Client-Verbindungen mit geringer Latenz
Das Web wurde größtenteils auf dem sogenannten Anfrage-/Antwort-Paradigma von HTTP aufgebaut. Ein Kunde lädt eine Webseite und es passiert nichts, bis der Nutzer auf die nächste Seite klickt. Ab etwa 2005 wurde das Web durch AJAX dynamischer. Die gesamte HTTP-Kommunikation wurde jedoch vom Client gesteuert, was eine Nutzerinteraktion oder regelmäßige Abfragen erforderte, um neue Daten vom Server zu laden.
Technologien, mit denen der Server Daten an den Client senden kann, sobald er weiß, dass neue Daten verfügbar sind, gibt es schon seit einiger Zeit. Beispiele wären „Push“ oder „Comet“. Einer der häufigsten Hacks, um den Anschein einer serverinitiierten Verbindung zu erwecken, wird als Long Polling bezeichnet. Beim Long Polling öffnet der Client eine HTTP-Verbindung zum Server, die bis zum Senden der Antwort geöffnet bleibt. Sobald der Server neue Daten hat, sendet er die Antwort. Andere Techniken umfassen Flash, XHR-Multipart-Anfragen und sogenannte htmlfiles. Long Polling und die anderen Techniken funktionieren recht gut. Sie verwenden sie täglich in Anwendungen wie Gmail-Chat.
Alle diese Lösungen haben jedoch ein gemeinsames Problem: Sie verursachen den Overhead von HTTP, was sie für Anwendungen mit geringer Latenz ungeeignet macht. Denken Sie an Mehrspieler-Ego-Shooter-Spiele im Browser oder an andere Onlinespiele mit einer Echtzeitkomponente.
Einführung in WebSocket: Sockets im Web
Die WebSocket definiert eine API, die „Socket“-Verbindungen zwischen einem Webbrowser und einem Server herstellt. Einfach ausgedrückt: Es besteht eine permanente Verbindung zwischen Client und Server und beide Seiten können jederzeit mit dem Senden von Daten beginnen.
Erste Schritte
Sie öffnen eine WebSocket-Verbindung einfach durch Aufrufen des WebSocket-Konstruktors:
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
Beachten Sie das ws:
. Das ist das neue URL-Schema für WebSocket-Verbindungen. Es gibt auch wss:
für sichere WebSocket-Verbindungen, genau wie https:
für sichere HTTP-Verbindungen verwendet wird.
Wenn Sie einige Ereignishandler direkt an die Verbindung anhängen, können Sie feststellen, wann die Verbindung geöffnet wird, eingehende Nachrichten empfangen werden oder ein Fehler auftritt.
Das zweite Argument akzeptiert optionale Unterprotokolle. Es kann sich dabei um einen String oder ein Array von Strings handeln. Jeder String sollte den Namen eines Subprotokolls darstellen. Der Server akzeptiert nur eines der übergebenen Subprotokolle im Array. Das zulässige Unterprotokoll kann durch Zugriff auf die Property protocol
des WebSocket-Objekts ermittelt werden.
Die Namen der Subprotokolle müssen zu den registrierten Subprotokollnamen in der IANA Registry gehören. Derzeit ist nur ein Subprotokollname (soap) registriert (Stand Februar 2012).
// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
Kommunikation mit dem Server
Sobald eine Verbindung zum Server besteht (das open
-Ereignis ausgelöst wird), können wir mit der send('your message')
-Methode des Verbindungsobjekts Daten an den Server senden. Bisher wurden nur Strings unterstützt, aber in der neuesten Spezifikation können jetzt auch binäre Nachrichten gesendet werden. Zum Senden von Binärdaten können Sie entweder das Blob
- oder das ArrayBuffer
-Objekt verwenden.
// Sending String
connection.send('your message');
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);
// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);
Ebenso kann der Server uns jederzeit Nachrichten senden. In diesem Fall wird der Rückruf onmessage
ausgelöst. Der Callback empfängt ein Ereignisobjekt und die tatsächliche Nachricht ist über die Property data
zugänglich.
WebSocket kann auch Binärnachrichten gemäß der neuesten Spezifikation empfangen. Binärframes können im Blob
- oder ArrayBuffer
-Format empfangen werden. Um das Format des empfangenen Binärcodes anzugeben, setze die Property „binaryType“ des WebSocket-Objekts auf „blob“ oder „arraybuffer“. Das Standardformat ist „blob“. Die Parameter „binaryType“ müssen beim Senden nicht abgeglichen werden.
// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};
Eine weitere neue Funktion von WebSocket sind Erweiterungen. Mithilfe von Erweiterungen können Frames beispielsweise komprimiert oder multiplexiert gesendet werden. Sie finden die vom Server akzeptierten Erweiterungen, indem Sie nach dem Open-Ereignis die Erweiterungseigenschaft des WebSocket-Objekts prüfen. Stand Februar 2012 gibt es noch keine offiziell veröffentlichte Spezifikation für Erweiterungen.
// Determining accepted extensions
console.log(connection.extensions);
Ursprungsübergreifende Kommunikation
Da WebSocket ein modernes Protokoll ist, ist die plattformübergreifende Kommunikation direkt in WebSocket integriert. Sie sollten zwar weiterhin nur mit vertrauenswürdigen Clients und Servern kommunizieren, aber WebSocket ermöglicht die Kommunikation zwischen Parteien in jeder Domain. Der Server entscheidet, ob er seinen Dienst für alle Clients oder nur für diejenigen verfügbar machen soll, die sich in einer Reihe von genau definierten Domains befinden.
Proxyserver
Jede neue Technologie bringt neue Probleme mit sich. Bei WebSocket ist es die Kompatibilität mit Proxyservern, die HTTP-Verbindungen in den meisten Unternehmensnetzwerken vermitteln. Das WebSocket-Protokoll verwendet das HTTP-Upgrade-System (normalerweise für HTTP/SSL), um eine HTTP-Verbindung zu einer WebSocket-Verbindung zu „aktualisieren“. Einige Proxyserver reagieren darauf und trennen die Verbindung. Selbst wenn ein bestimmter Client das WebSocket-Protokoll verwendet, ist es also möglicherweise nicht möglich, eine Verbindung herzustellen. Das macht den nächsten Abschnitt noch wichtiger.
WebSockets jetzt verwenden
WebSocket ist noch eine junge Technologie und nicht in allen Browsern vollständig implementiert. Sie können WebSocket jedoch bereits jetzt mit Bibliotheken verwenden, die einen der oben genannten Fallbacks verwenden, wenn WebSocket nicht verfügbar ist. Eine Bibliothek, die in diesem Bereich sehr beliebt geworden ist, ist socket.io. Sie enthält eine Client- und eine Serverimplementierung des Protokolls und Fallbacks. (socket.io unterstützt Stand Februar 2012 noch keine binären Nachrichten.) Es gibt auch kommerzielle Lösungen wie PusherApp, die sich ganz einfach in jede Webumgebung einbinden lassen, indem eine HTTP-API zum Senden von WebSocket-Nachrichten an Clients bereitgestellt wird. Aufgrund der zusätzlichen HTTP-Anfrage gibt es immer einen zusätzlichen Overhead im Vergleich zu reinen WebSockets.
Serverseite
Die Verwendung von WebSockets schafft ein ganz neues Nutzungsmuster für serverseitige Anwendungen. Traditionelle Serverstacks wie LAMP sind zwar auf den HTTP-Anfrage/-Antwort-Zyklus ausgelegt, können aber oft nicht gut mit einer großen Anzahl offener WebSocket-Verbindungen umgehen. Um eine große Anzahl von Verbindungen gleichzeitig offen zu halten, ist eine Architektur erforderlich, die eine hohe Parallelität bei geringen Leistungskosten bietet. Solche Architekturen basieren in der Regel auf Threading oder sogenannter nicht blockierender E/A.
Serverseitige Implementierungen
- Node.js
- Java
- Ruby
- Python
- Erlang
- C++
- .NET
Protokollversionen
Das Wire-Protokoll (ein Handshake und die Datenübertragung zwischen Client und Server) für WebSocket ist jetzt RFC6455. Die neueste Version von Chrome und Chrome für Android sind vollständig mit RFC6455 kompatibel, einschließlich binärer Nachrichten. Firefox ist mit Version 11 und Internet Explorer mit Version 10 kompatibel. Sie können auch weiterhin ältere Protokollversionen verwenden. Dies wird jedoch nicht empfohlen, da sie bekanntermaßen anfällig sind. Wenn Sie Serverimplementierungen für ältere Versionen des WebSocket-Protokolls haben, empfehlen wir Ihnen, ein Upgrade auf die neueste Version durchzuführen.
Anwendungsfälle
Verwenden Sie WebSocket, wenn Sie eine Verbindung mit wirklich niedriger Latenz und nahezu in Echtzeit zwischen Client und Server benötigen. Beachten Sie, dass Sie möglicherweise Ihre serverseitigen Anwendungen neu entwickeln müssen, wobei Sie einen neuen Schwerpunkt auf Technologien wie Ereigniswarteschlangen legen. Beispiele für Anwendungsfälle:
- Online-Mehrspielerspiele
- Chat-Apps
- Live-Ticker für Sport
- Echtzeitaktualisierung von Social-Media-Streams
Demos
- Plink
- Paint With Me
- Pixelatr
- Gestrichelte Linie
- Massively Multiplayer Online Crossword
- Ping-Server (in den Beispielen oben verwendet)
- Beispiel für HTML5-Demos