Einführung in WebSockets – Sockets im Web

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

Das Web basiert größtenteils auf dem sogenannten Anfrage- und Antwortmodell von HTTP. Ein Client lädt eine Webseite hoch und nichts passiert, bis der Nutzer auf die nächste Seite klickt. Ungefähr im Jahr 2005 machte AJAX das Web dynamischer. Dennoch wurde die gesamte HTTP-Kommunikation vom Client gesteuert, der eine Nutzerinteraktion oder regelmäßige Abfragevorgänge erforderte, 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. Sie heißen „Push“ oder „Comet“. Einer der häufigsten Hacks, die den Anschein einer vom Server initiierten Verbindung erzeugen, wird als Long Polling bezeichnet. Bei langen Abfragen öffnet der Client eine HTTP-Verbindung zum Server. Sie bleibt geöffnet, bis die Antwort gesendet wird. Immer wenn der Server tatsächlich neue Daten hat, sendet er die Antwort. Andere Techniken sind Flash, XHR-Multipart-Anfragen und sogenannte HTMLfiles. Lange Abfragen und die anderen Techniken funktionieren ziemlich gut. Sie verwenden sie täglich in Anwendungen wie dem Google Mail-Chat.

All diese Behelfslösungen haben jedoch ein gemeinsames Problem: Sie nutzen HTTP, wodurch sie nicht gut für Anwendungen mit niedriger Latenz geeignet sind. Denken Sie an Multiplayer-Ego-Shooter-Spiele im Browser oder andere Onlinespiele mit Echtzeitkomponenten.

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, 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. Es gibt auch wss: für sichere WebSocket-Verbindungen, genauso wie https: für sichere HTTP-Verbindungen verwendet wird.

Wenn Sie einige Event-Handler sofort an die Verbindung anhängen, können Sie erkennen, wann die Verbindung geöffnet wurde, eingehende Nachrichten empfangen wurden oder ein Fehler aufgetreten ist.

Das zweite Argument akzeptiert optionale Subprotokolle. Das kann ein String oder ein Array von Strings sein. Jeder String sollte einen Subprotokollnamen darstellen und der Server akzeptiert nur eines der übergebenen Subprotokolle im Array. Das zulässige Subprotokoll kann durch Zugriff auf die protocol-Eigenschaft des WebSocket-Objekts bestimmt werden.

Die Subprotokollnamen müssen einer der in der IANA-Registry registrierten Subprotokollnamen sein. 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 eine Verbindung zum Server besteht (wenn das open-Ereignis ausgelöst wird), können wir mit dem Senden von Daten an den Server beginnen. Dazu verwenden wir die Methode send('your message') für das Verbindungsobjekt. Bisher wurden nur Strings unterstützt, in der neuesten Spezifikation kann sie jetzt aber auch Binärnachrichten senden. Zum Senden von Binärdaten können Sie das Objekt Blob oder ArrayBuffer 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 uns der Server jederzeit Nachrichten senden. Jedes Mal, wenn dies geschieht, wird der onmessage-Callback ausgelöst. Der Callback empfängt ein Ereignisobjekt und die eigentliche Nachricht kann über die data-Eigenschaft aufgerufen werden.

WebSocket kann in der neuesten Spezifikation auch Binärmeldungen empfangen. Binärframes können im Format Blob oder ArrayBuffer empfangen werden. Setze die binaryType-Eigenschaft des WebSocket-Objekts auf "blob" oder auf "arraybuffer", um das Format der empfangenen Binärdatei anzugeben. Das Standardformat ist „blob“. (Sie müssen den binaryType-Parameter beim Senden nicht abstimmen.)

// 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 neu hinzugefügte Funktion von WebSocket sind Erweiterungen. Mithilfe von Erweiterungen ist es möglich, Frames wie komprimierte oder gevielfältige Frames usw. zu senden. Welche Erweiterungen vom Server akzeptiert werden, erfahren Sie in der Erweiterungseigenschaft des WebSocket-Objekts nach dem Open-Ereignis. Stand Februar 2012 gibt es noch keine offiziell veröffentlichten Erweiterungsspezifikationen.

// 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 trotzdem sicherstellen, dass Sie nur mit vertrauenswürdigen Clients und Servern kommunizieren, WebSocket ermöglicht jedoch die Kommunikation zwischen Parteien in jeder Domain. Der Server entscheidet, ob sein Dienst für alle Clients verfügbar ist oder nur für diejenigen, die sich in einer Gruppe klar definierter Domains befinden.

Proxyserver

Jede neue Technologie bringt neue Probleme mit sich. Im Fall von WebSocket ist es die Kompatibilität mit Proxyservern, die HTTP-Verbindungen in den meisten Unternehmensnetzwerken vermitteln. Das WebSocket-Protokoll verwendet das HTTP-Upgradesystem, das normalerweise für HTTP/SSL genutzt wird, für das "Upgrade" einer HTTP-Verbindung auf eine WebSocket-Verbindung. Einige Proxy-Server können damit nicht umgehen und unterbrechen 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 noch nicht vollständig in allen Browsern implementiert. Sie können WebSocket jedoch sofort mit Bibliotheken verwenden, die eines der oben genannten Fallbacks verwenden, wenn WebSocket nicht verfügbar ist. Eine in dieser Domain sehr beliebte Bibliothek ist socket.io. Sie enthält eine Client- und Serverimplementierung des Protokolls sowie Fallbacks. Socket.io unterstützt seit Februar 2012 noch keine Binärmitteilungen. 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 entsteht immer zusätzlicher Aufwand im Vergleich zum reinen WebSocket.

Serverseite

Durch die Verwendung von WebSocket entsteht 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 hohe Nebenläufigkeit bei geringen Kosten für die Leistung bietet. Solche Architekturen sind in der Regel entweder um Threading oder sogenannte nicht blockierende E/A aufgebaut.

Serverseitige Implementierungen

Protokollversionen

Das Wire-Protokoll (ein Handshake und die Datenübertragung zwischen Client und Server) für WebSocket ist jetzt RFC6455. Die aktuelle Version von Chrome und Chrome für Android sind vollständig kompatibel mit RFC6455, einschließlich Binärmitteilungen. Firefox wird ab Version 11 ebenfalls kompatibel sein, Internet Explorer ab Version 10. Sie können auch ältere Protokollversionen verwenden, aber dies wird 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.

Anwendungsfälle

Verwenden Sie WebSocket, wenn Sie eine sehr niedrige Latenz, also fast eine Echtzeitverbindung, zwischen Client und Server benötigen. Denken Sie daran, dass dies möglicherweise auch dazu führt, dass Sie die Erstellung Ihrer serverseitigen Anwendungen mit einem neuen Schwerpunkt auf Technologien wie Ereigniswarteschlangen überdenken müssen. Hier einige Beispiele für Anwendungsfälle:

  • Multiplayer-Onlinespiele
  • Chat-Apps
  • Live-Sport-Ticker
  • Echtzeitaktualisierungen von sozialen Streams

Demos

Verweise