Das Problem: Client-Server- und Server-Client-Verbindungen mit geringer Latenz
Das Web wurde weitgehend 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. Dennoch wurde die gesamte HTTP-Kommunikation vom Client gesteuert, der eine Nutzerinteraktion oder regelmäßiges Polling erfordert, um neue Daten vom Server zu laden.
Technologien, die es dem Server ermöglichen, in dem Moment, in dem er weiß, dass neue Daten verfügbar sind, an den Client zu senden, gibt es schon seit geraumer 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. Immer wenn der Server tatsächlich neue Daten hat, sendet er die Antwort. Andere Techniken sind Flash, XHR-Multipart-Anfragen und sogenannte htmlfiles. Long Polling und die anderen Techniken funktionieren recht gut. Sie verwenden sie täglich in Anwendungen wie dem Google Mail-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-Spezifikation definiert eine API, die „Socket“-Verbindungen zwischen einem Webbrowser und einem Server herstellt. Kurz gesagt: Es besteht eine dauerhafte Verbindung zwischen Client und Server und beide Parteien 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 die 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 akzeptierte Subprotokoll kann durch den Zugriff auf die protocol
-Eigenschaft des WebSocket-Objekts bestimmt werden.
Die Namen der Subprotokolle müssen zu den registrierten Subprotokollnamen in der IANA Registry gehören. Derzeit gibt es nur einen seit Februar 2012 registrierten Subprotokollnamen (soap).
// 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 wir eine Verbindung zum Server haben (wenn 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 eigentliche Nachricht kann über die data
-Eigenschaft aufgerufen werden.
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
Als modernes Protokoll ist die ursprungsübergreifende Kommunikation direkt in WebSocket enthalten. 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. Daher ist es eventuell nicht möglich, eine Verbindung herzustellen, selbst wenn ein bestimmter Client das WebSocket-Protokoll verwendet. Daher ist der nächste Abschnitt noch wichtiger :)
WebSockets noch heute 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 leicht in jede Webanwendung integrieren 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. Während herkömmliche Server-Stacks wie LAMP nach dem HTTP-Anforderungs-/Antwortzyklus konzipiert sind, eignen sie sich häufig nicht optimal für eine große Anzahl offener WebSocket-Verbindungen. 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 wird ab Version 11 ebenfalls kompatibel sein, Internet Explorer ab Version 10. 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
- Plinken
- Paint With Me
- Pixelatr
- Gestrichelte Linie
- Massively-Multiplayer-Online-Kreuzworträtsel
- Ping-Server (in den obigen Beispielen verwendet)
- Beispiel für HTML5-Demos