Verwenden der Websocket-Komprimierung mit uWebSockets.js und Websocket-Sharp

11

Wir haben ein Handyspiel, das Websocket für Verbindungen verwendet. Der Server ist eine Node.js-App, die die Bibliothek uWebSockets.js verwendet, und der Client ist eine Unity-App, die die Websocket-Sharp- Bibliothek verwendet. Beide spielen gut zusammen und wir hatten kein Problem mit ihnen.

Kürzlich wollten wir die Websocket-Komprimierung aktivieren . Beide Bibliotheken gaben an, dass sie die Erweiterung für die Komprimierung pro Nachricht unterstützen, aber es scheint, dass etwas mit ihnen nicht kompatibel ist. Denn wenn wir die Komprimierung konfigurieren, wird die Websocket-Verbindung beim Handshake sofort geschlossen.

Wir haben auch den Client mit der ws- Bibliothek getestet und es wird ein Beispiel für die Komprimierung mit demselben Ergebnis bereitgestellt. Wir haben versucht, an den ws-Komprimierungsoptionen zu basteln, und festgestellt, dass beim Kommentieren der Option serverMaxWindowBits (standardmäßig der ausgehandelte Wert) die Verbindung hergestellt werden konnte und das Senden und Empfangen von Nachrichten problemlos funktioniert. Wir haben auch nach der Steuerung der serverMaxWindowBits in uWebsockets gefragt.

Das letzte, was wir versucht haben, war die Verbindung eines minimalen uWS-Servers und eines websocketscharfen Clients. Hier ist der Code für den Server:

const uWS = require('uWebSockets.js');
const port = 5001;

const app = uWS.App({
    }).ws('/*', {
        /* Options */
        compression: 1, // Setting shared compression method
        maxPayloadLength: 4 * 1024,
        idleTimeout: 1000,
        /* Handlers */
        open: (ws, req) => {
            console.log('A WebSocket connected via URL: ' + req.getUrl() + '!');
        },
        message: (ws, message, isBinary) => {
            /* echo every message received */
            let ok = ws.send(message, isBinary);
        },
        drain: (ws) => {
            console.log('WebSocket backpressure: ' + ws.getBufferedAmount());
        },
        close: (ws, code, message) => {
            console.log('WebSocket closed');
        }
    }).any('/*', (res, req) => {
        res.end('Nothing to see here!');
    }).listen(port, (token) => {
        if (token) {
            console.log('Listening to port ' + port);
        } else {
            console.log('Failed to listen to port ' + port);
        }
    });

Hier ist der Client-Code:

using System;
using WebSocketSharp;

namespace Example
{
  public class Program
  {
    public static void Main (string[] args)
    {
      using (var ws = new WebSocket ("ws://localhost:5001")) {
        ws.OnMessage += (sender, e) =>
            Console.WriteLine ("server says: " + e.Data);

        ws.Compression = CompressionMethod.Deflate; // Turning on compression
        ws.Connect ();

        ws.Send ("{\"comm\":\"example\"}");
        Console.ReadKey (true);
      }
    }
  }
}

Wenn wir den Server und den Client ausgeführt haben, gibt der Client den folgenden Fehler aus:

Fehler | WebSocket.checkHandshakeResponse | Der Server hat 'server_no_context_takeover' nicht zurückgesendet. Fatal | WebSocket.doHandshake | Enthält einen ungültigen Sec-WebSocket-Extensions-Header.

Es schien, dass der Client den Header server_no_context_takeover erwartet und keinen erhalten hat. Wir haben die uWebsockets- Quelle (C ++ - Teil des Moduls uWebsockets.js) überprüft und eine kommentierte Bedingung für das Zurücksenden des Headers server_no_context_takeover gefunden. Also haben wir die Bedingung auskommentiert und uWebsockets.js erstellt und erneut getestet, um den folgenden Fehler im Client zu finden:

WebSocketSharp.WebSocketException: Der Header eines Frames kann nicht aus dem Stream gelesen werden.

Irgendwelche Vorschläge, wie diese beiden Bibliotheken zusammenarbeiten können?

Arash
quelle
Ich bin mir nicht sicher, ob es überhaupt hilft, aber socket.io komprimiert standardmäßig. Ich weiß, dass das beste HTTP 2 ziemlich gute Unterstützung für socket.io - Websockets bietet.
Samuel G
@SamuelG Danke, aber die Verwendung von socket.io ist keine Option, da wir derzeit mehr als 5.000 gleichzeitige Verbindungen mit minimalen Ressourcen verarbeiten.
Koorosh Pasokhi
@KooroshPasokhi Was hat Ihre Argumentation ergeben, dass socket.io Ihre Last nicht bewältigen kann oder ressourcenintensiv ist? Würde gerne mehr über deine Tests erfahren.
Samuel G
@SamuelG Kommentare sind nicht für Diskussionen gedacht, aber sagen wir, die Hauptgründe sind, dass der Fokus von socket.io nicht auf der Leistung liegt und wir keine höheren Abstraktionen in socket.io benötigen.
Koorosh Pasokhi

Antworten:

3

Update: Basierend auf dem Einlesen des Codes in uWebSockets.jsmüssten Änderungen vorgenommen werden, um alle Parameter websocket-sharpzu aktivieren, die zum Aktivieren der Komprimierung erforderlich sind. In Vertx, einem Hochleistungs-Java-Server, funktionieren die folgenden Einstellungen mit Unity-kompatibel websocket-sharpfür die Komprimierung:

vertx.createHttpServer(new HttpServerOptions()
                .setMaxWebsocketFrameSize(65536)
                .setWebsocketAllowServerNoContext(true)
                .setWebsocketPreferredClientNoContext(true)
                .setMaxWebsocketMessageSize(100 * 65536)
                .setPerFrameWebsocketCompressionSupported(true)
                .setPerMessageWebsocketCompressionSupported(true)
                .setCompressionSupported(true));

Vorher:

Der Fehler ist real, websocket-sharpunterstützt nur permessage-deflate, verwenden Sie stattdessen DEDICATED_COMPRESSOR( compression: 2).

DoctorPangloss
quelle
Leider hat das Setzen der Komprimierungsmethode auf 2 die Fehlermeldung nicht geändert :(
Koorosh Pasokhi
Dann gibt es ein zugrunde liegendes Problem mit uWebSockets. Ich verwende Javas Vertx-Websockets mit Komprimierung mit WebSocketSharp im Client, der mehr Konfigurationsfelder hat, und es funktioniert einfach.
DoctorPangloss
Versuchen Sie vielleicht, einen Test direkt in uWebSockets.js zu erstellen, der das Verhalten der WebSocket.csHeader-Zurückweisung reproduziert ? DEDICATED_COMPRESSORsollte wirklich funktionieren!
DoctorPangloss
Welchen Test meinst du? Die Architektur von uWebSockets.js ist etwas kompliziert. Es hat drei Ebenen in JS, C ++ und C geschrieben, was das Debuggen schwierig macht.
Koorosh Pasokhi
Ich meine, einen Test im Projektverzeichnis von uWebSockets.js zu erstellen, der die Handshake-Websocket-scharfen Funktionen reproduziert. Dies können Sie möglicherweise feststellen, indem Sie ihn an einen kompatiblen Server anschließen und sehen, was passiert.
DoctorPangloss