JavaScript/WebSocket/WebSockets im Browser

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Alle modernen Browser unterstützen das WebSocket Protokoll. Zu Beginn der 2010er Jahre war das noch etwas schwieriger, weil das Protokoll sich entwickelte und die Versionen nicht kompatibel waren. Seit etlichen Jahren wird die in RfC 6544 spezifizierte Version 13 von allen Browsern, selbst dem Internet Explorer 10, unterstützt.

Um eine WebSocket-Verbindung zu öffnen, verwenden Sie eine URL, die das wss:// Protokoll verwendet. wss steht für WebSocket Secure, verwendet also analog zu https:// eine zertifikatbasierende Transportverschlüsselung (TLS). Es gibt auch das ws://-Protokoll ohne Verschlüsselung, aber von dessen Nutzung wird abgeraten, es kann vom Browser oder Server verweigert werden und wenn Ihre HTML-Seite - wie empfohlen - über https:// ausgeliefert wird, können Sie Ressourcen ohnehin nur über eine TLS-Verbindung abrufen.

Öffnen einer Websocket-Verbindung
const ws = new WebSocket("wss://www.example.org/socketserver", "chat");
ws.onopen = function(openEvent) {
   console.log("WebSocket ist mit dem " + ws.protocol + " Protokoll geöffnet");
   ws.send("Hello Server!");
}
ws.onmessage = function(messageEvent) {
   console.log(messageEvent.data);
};
ws.onclose = function(closeEvent) {
   console.log("WebSocket wurde geschlossen");
};

Das Beispiel erstellt eine WebSocket-Verbindung zu der URL wss://www.example.org/socketserver. Was Sie auf dem Server tun müssen, um eine solche URL anbieten zu können, werden wir später erklären.

Die Art und Weise, wie die zu übertragenden Daten von einer Seite der WebSocket-Verbindung zur anderen kommen, ist durch das WebSocket-Protokoll festgelegt und wird vom Browser übernommen. Welche Daten aber zu welchem Zweck übertragen werden und welche Struktur sie haben, ist eine andere Frage. Um dies festzulegen, benötigt man ein untergeordnetes Protokoll: das Subprotokoll. Das Beispiel verwendet ein Subprotokoll namens "chat". Dazu später mehr.

Wenn der Server die Verbindung akzeptiert, ist der WebSocket geöffnet und auf dem WebSocket-Objekt wird das open-Event ausgelöst. Ab jetzt kann die Verbindung zum Senden und Empfangen von Daten verwendet werden. Sie können für das open-Event einen Eventhandler registrieren, um in Ihrer Anwendung die Kommunikation freizuschalten.

Zum Senden eines Datenblocks an den Server verwenden Sie die send-Methode des WebSocket-Objekts. Wenn Sie, wie hier, eine Zeichenkette übergeben, wird sie vom WebSocket-Protokoll in Form einer UTF-8 codierten Bytefolge übertragen, Sie können also beliebige Unicode-Zeichenketten übergeben. Die Übertragung von JavaScript-Objekten kann auf diesem Weg ebenfalls erfolgen, indem Sie daraus eine JSON-Zeichenkette erstellen.

Alternativ können Sie auch ein Blob-, ArrayBuffer-, TypedArray- oder DataView-Objekte übergeben, diese werden als binäre Daten übertragen.

Um Daten zu empfangen, müssen Sie einen Eventhandler für das message-Event registrieren. Sobald ein Datenpaket vom Server angekommen ist, wird das Event ausgelöst. Ihr EventHandler erhält ein MessageEvent-Objekt übergeben und findet in dessen data-Eigenschaft die übertragenen Daten vor. Dabei kann es sich um eine Zeichenkette handeln, oder um binäre Daten. Binäre Daten erhalten Sie standardmäßig in Form eines Blob-Objekts, aber Sie können mit der Eigenschaft binaryType des WebSocket-Objekts auch festlegen, dass Sie die Daten als ArrayBuffer erhalten möchten.

Was Sie mit den erhaltenen Daten dann tun, ist Ihnen überlassen. Wie diese Daten aussehen und strukturiert sind, wird durch das Subprotokoll festgelegt, das Ihre Verbindung nutzt.

Um die WebSocket-Verbindung zu schließen, verwenden Sie die close-Methode des WebSocket-Objekts. Der Browser handelt daraufhin mit dem Server das Ende der Verbindung aus. Nachdem die Verbindung geschlossen ist, wird auf dem WebSocket-Objekt das close-Event ausgelöst. Die Verbindung kann aber genausogut vom Server geschlossen werden. Auch in diesem Fall empfangen Sie nach dem Schließen ein close-Event. Daten, die Sie während des Close-Handshake mit send() gesendet haben, bleiben im Pufferspeicher des WebSocket-Objekts stehen. Wieviele Daten sich dort befinden, erfahren Sie über die bufferedAmount-Eigenschaft des WebSocket-Objekts.

Subprotokolle

Wie schon angedeutet, legt das Subprotokoll der Zweck und den Aufbau der ausgetauschten Daten fest. Ein solches Subprotokoll können Sie sich selbst ausdenken, wenn Sie Client und Server selbst in der Hand haben. Für den Fall, dass Sie eine öffentliche Schnittstelle anbieten möchten, sollten Sie aber einen Blick auf die Subprotocol-Registry der IANA werfen, wo bereits etliche Subprotokolle registriert sind. Die IANA empfiehlt, dass Subprotokolle, das öffentlich nutzbar sein sollen, dort registriert werden.

Wenn Sie vom Server ein bestimmtes Protokoll für eine Verbindung erwarten, können Sie den Namen dieses Protokolls als zweiten Parameter an den WebSocket-Konstruktor übergeben. Wenn es unterschiedliche Varianten eines solchen Protokolls gibt, können Sie auch ein Array übergeben, in dem Sie die Varianten eintragen, die Ihr Client versteht. Der Server wählt dann eins aus, das er kennt und baut die Verbindung auf. Kennt er kein von Ihnen gewünschtes Protokoll, wird die Verbindung abgelehnt und auf dem WebSocket-Objekt wird zunächst ein error- und dann ein close-Event ausgelöst. Ist das Protokoll unterstützt, erhalten Sie ein open-Event und finden das vom Server ausgewählte Protokoll in der protocol-Eigenschaft des WebService-Objekts vor.

Für eigene Versuche und für Systeme, an denen nicht mehrere Hersteller beteiligt sind, kann man auf die Angabe des Protokolls verzichten und einfach davon ausgehen, dass das von Ihnen vorgesehene Subprotokoll von Server und Client verwendet wird. Wie gesagt: kann. Wenn Sie sich mit einem fremden Server verbinden wollen und dieser von Ihnen erwartet, dass Sie ein bestimmtes Subprotokoll anfordern, dann müssen Sie das auch tun.

Sie können den Protokoll-Parameter aber auch nutzen, um unter einer URL unterschiedliche Kommunikationskanäle anzubieten. Stellen Sie sich zum Beispiel einen Spieleserver vor. Er könnte die Protokolle chat, lobby und game unterstützen. Ein Client könnte nun eine WebSocket-Verbindung mit dem lobby-Protokoll eröffnen und darüber erfahren, welche Spiele gerade gespielt werden und ob in Spielen Mitspieler gesucht werden. Über eine zweite WebSocket-Verbindung, die das chat-Protokoll nutzt, könnte der öffentliche Chat des Spieleservers erreichbar sein. Und sobald man sich mit einem konkreten Spiel verbindet, kann man - mit einer geeigneten URL - zwei WebSockets mit den game und chat-Protokollen für diesen Spieltisch öffnen. Es ist Aufgabe des Servers, mit den unterschiedlichen Verbindungen dann richtig umzugehen. Allerdings erzeugt dieses Vorgehen auch eine Menge an offenen WebSocket-Verbindungen und man könnte statt dessen auch überlegen, nur eine einzige Verbindung zu öffnen und das Subprotokoll so zu gestalten, dass die unterschiedlichen Kommunikationskanäle darüber gemeinsam übertragen werden können.