Introducing WebSockets - Bringing sockets to the web

Das Problem: Client-Server- und Server-Client-Verbindungen mit niedriger Latenz

Das Web basiert größtenteils auf dem sogenannten Anfrage-/Antwort-Paradigma von HTTP. Ein Client lädt eine Webseite und dann passiert nichts, bis der Nutzer auf die nächste Seite klickt. Um 2005 herum 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, die es dem Server ermöglichen, Daten an den Client zu senden, sobald er weiß, dass neue Daten verfügbar sind, gibt es schon seit geraumer Zeit. Sie heißen „Push“ oder „Comet“. Eine der gängigsten Methoden, um die Illusion einer vom Server initiierten Verbindung zu erzeugen, ist das sogenannte Long Polling. Beim Long Polling öffnet der Client eine HTTP-Verbindung zum Server, die bis zum Senden der Antwort offen 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 dem Chat in Gmail.

Alle diese Behelfslösungen haben jedoch ein Problem: Sie verursachen den Overhead von HTTP, was sie für Anwendungen mit niedriger Latenz ungeeignet macht. Denken Sie an Multiplayer-Ego-Shooter-Spiele im Browser oder an andere Onlinespiele mit einer Echtzeitkomponente.

Introducing WebSocket: Bringing sockets to the web

In der WebSocket-Spezifikation wird eine API definiert, die „Socket“-Verbindungen zwischen einem Webbrowser und einem Server herstellt. Einfach ausgedrückt: Es gibt eine dauerhafte Verbindung zwischen dem Client und dem Server und beide Parteien können jederzeit mit dem Senden von Daten beginnen.

Erste Schritte

Sie öffnen eine WebSocket-Verbindung, indem Sie einfach den WebSocket-Konstruktor aufrufen:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Beachten Sie die ws:. Dies ist das neue URL-Schema für WebSocket-Verbindungen. Außerdem gibt es wss: für sichere WebSocket-Verbindungen, so wie https: für sichere HTTP-Verbindungen verwendet wird.

Wenn Sie einige Event-Handler sofort 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 ein String oder ein Array von Strings sein. Jeder String sollte einen Unterprotokollnamen darstellen und der Server akzeptiert nur eines der übergebenen Unterprotokolle im Array. Das akzeptierte Unterprotokoll kann durch Zugriff auf das Attribut protocol des WebSocket-Objekts ermittelt werden.

Die Namen der Unterprotokolle müssen in der IANA-Registry registriert sein. Derzeit (Stand: Februar 2012) ist nur ein Unterprotokollname (soap) registriert.

// 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 (wenn das Ereignis open ausgelöst wird), können wir mit der Methode send('your message') für das Verbindungsobjekt Daten an den Server senden. Früher 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 jederzeit Nachrichten an uns senden. In diesem Fall wird der Callback onmessage ausgelöst. Der Callback empfängt ein Ereignisobjekt und die eigentliche Nachricht ist über das Attribut data zugänglich.

WebSocket kann in der neuesten Spezifikation auch binäre Nachrichten empfangen. Binäre Frames können im Format Blob oder ArrayBuffer empfangen werden. Wenn Sie das Format der empfangenen Binärdatei angeben möchten, legen Sie das Attribut „binaryType“ des WebSocket-Objekts entweder auf „blob“ oder „arraybuffer“ fest. Das Standardformat ist „blob“. (Sie müssen den Parameter „binaryType“ beim Senden nicht abgleichen.)

// 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 komprimiert, gemultiplext usw. gesendet werden. Sie können die vom Server akzeptierten Erweiterungen ermitteln, indem Sie nach dem Open-Ereignis die Erweiterungseigenschaft des WebSocket-Objekts prüfen. Bis Februar 2012 gab es noch keine offiziell veröffentlichte Spezifikation für Erweiterungen.

// Determining accepted extensions
console.log(connection.extensions);

Ursprungsübergreifende Kommunikation

Da es sich um ein modernes Protokoll handelt, ist die ursprungsübergreifende Kommunikation direkt in WebSocket integriert. Sie sollten zwar weiterhin darauf achten, nur mit Clients und Servern zu kommunizieren, denen Sie vertrauen, aber WebSocket ermöglicht die Kommunikation zwischen Parteien in jeder Domain. Der Server entscheidet, ob er seinen Dienst allen Clients oder nur denjenigen zur Verfügung stellt, die sich in einer Reihe von klar definierten Domains befinden.

Proxyserver

Jede neue Technologie bringt neue Probleme mit sich. Bei WebSocket ist es die Kompatibilität mit Proxyservern, die in den meisten Unternehmensnetzwerken HTTP-Verbindungen vermitteln. Das WebSocket-Protokoll verwendet das HTTP-Upgrade-System (das normalerweise für HTTP/SSL verwendet wird), um eine HTTP-Verbindung auf eine WebSocket-Verbindung zu aktualisieren. Einige Proxyserver können damit nicht umgehen und trennen die Verbindung. Selbst wenn ein bestimmter Client das WebSocket-Protokoll verwendet, ist es daher möglicherweise nicht möglich, eine Verbindung herzustellen. Deshalb ist der nächste Abschnitt umso wichtiger :)

WebSockets verwenden

WebSocket ist noch eine junge Technologie und nicht in allen Browsern vollständig implementiert. Sie können WebSocket jedoch schon heute mit Bibliotheken verwenden, die eine der oben genannten Fallbacks verwenden, wenn WebSocket nicht verfügbar ist. Eine in diesem Bereich sehr beliebte Bibliothek ist socket.io. Sie enthält eine Client- und eine Serverimplementierung des Protokolls und bietet Fallbacks (socket.io unterstützt seit Februar 2012 noch keine binären Nachrichten). Es gibt auch kommerzielle Lösungen wie PusherApp, die sich einfach in jede Webumgebung einbinden lassen, indem sie eine HTTP API zum Senden von WebSocket-Nachrichten an Clients bereitstellen. Aufgrund der zusätzlichen HTTP-Anfrage entsteht im Vergleich zu reinem WebSocket immer ein zusätzlicher Aufwand.

Serverseite

Die Verwendung von WebSocket schafft ein völlig neues Nutzungsmuster für serverseitige Anwendungen. Herkömmliche Server-Stacks wie LAMP sind auf den HTTP-Anfrage-/Antwortzyklus ausgelegt, können aber oft nicht gut mit einer großen Anzahl offener WebSocket-Verbindungen umgehen. Wenn eine große Anzahl von Verbindungen gleichzeitig offen gehalten werden soll, ist eine Architektur erforderlich, die eine hohe Nebenläufigkeit bei geringen Leistungskosten ermöglicht. Solche Architekturen sind in der Regel entweder auf Threading oder auf sogenannte nicht blockierende E/A ausgelegt.

Serverseitige Implementierungen

Protokollversionen

Das Wire-Protokoll (ein Handshake und die Datenübertragung zwischen Client und Server) für WebSocket ist jetzt RFC6455. Die neuesten Versionen von Chrome und Chrome für Android sind vollständig mit RFC6455 kompatibel, einschließlich binärer Nachrichten. Firefox ist ab Version 11 und Internet Explorer ab Version 10 kompatibel. Sie können 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 aktuelle Version durchzuführen.

Anwendungsfälle

Verwenden Sie WebSocket, wenn Sie eine Verbindung mit sehr niedriger Latenz und nahezu in Echtzeit zwischen dem Client und dem Server benötigen. Möglicherweise müssen Sie die Entwicklung Ihrer serverseitigen Anwendungen überdenken und sich auf Technologien wie Ereigniswarteschlangen konzentrieren. Hier einige Beispielanwendungsfälle:

  • Online-Mehrspielerspiele
  • Chat-Anwendungen
  • Live-Sportticker
  • In Echtzeit aktualisierte Social-Media-Streams

Demos

Verweise