Utilisation de la compression Websocket avec uWebSockets.js et Websocket-Sharp

11

Nous avons un jeu mobile utilisant websocket pour les connexions. Le serveur est une application Node.js utilisant bibliothèque uWebSockets.js et le client est une application Unity utilisant la bibliothèque Websocket-Sharp . Ils jouent tous les deux bien ensemble et nous n'avons rencontré aucun problème avec eux.

Récemment, nous voulions activer la compression Websocket . Les deux bibliothèques ont déclaré qu'elles prennent en charge l'extension de compression par message, mais il semble qu'il y ait quelque chose d'incompatible avec elles. Parce que lorsque nous configurons pour utiliser la compression, la connexion websocket se ferme immédiatement lors de la prise de contact.

Nous avons également testé le client avec ws bibliothèque et il est fourni un exemple de compression avec le même résultat. Nous avons essayé de bricoler les options de compression ws et avons constaté que lorsque nous commentions l'option serverMaxWindowBits (par défaut la valeur négociée), la connexion pouvait être établie et l'envoi et la réception de messages fonctionnaient sans problème. Nous avons également demandé comment contrôler les serverMaxWindowBits dans uWebsockets.

La dernière chose que nous avons essayée a été de connecter un serveur uWS minimal et un client websocket-sharp. Voici le code du serveur:

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);
        }
    });

Voici le code client:

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);
      }
    }
  }
}

Lorsque nous avons exécuté le serveur et le client, le client émet l'erreur suivante:

Erreur | WebSocket.checkHandshakeResponse | Le serveur n'a pas renvoyé 'server_no_context_takeover'. Fatal | WebSocket.doHandshake | Inclut un en-tête Sec-WebSocket-Extensions non valide.

Il semblait que le client attendait l'en-tête server_no_context_takeover et n'en avait pas reçu. Nous avons examiné la source uWebsockets (partie C ++ du module uWebsockets.js) et trouvé une condition commentée pour renvoyer l'en-tête server_no_context_takeover. Nous avons donc décommenté la condition et créé uWebsockets.js et testé à nouveau pour rencontrer l'erreur suivante dans le client:

WebSocketSharp.WebSocketException: l'en-tête d'une trame ne peut pas être lu à partir du flux.

Avez-vous des suggestions pour faire fonctionner ces deux bibliothèques ensemble?

Arash
la source
Pas sûr que cela aide du tout, mais socket.io se comprime par défaut. Je sais que le meilleur HTTP 2 a un assez bon support pour socket.io - websockets.
Samuel G
@SamuelG Merci, mais utiliser socket.io n'est pas une option car nous traitons actuellement 5k + connexions simultanées avec un minimum de ressources.
Koorosh Pasokhi
@KooroshPasokhi qu'est-ce qui a conclu votre raisonnement selon lequel socket.io ne pourrait pas gérer votre charge ou consomme beaucoup de ressources? J'aimerais en savoir plus sur vos tests.
Samuel G
@SamuelG Les commentaires ne sont pas pour les discussions, mais disons que les raisons principales sont que socket.io n'est pas la performance et que nous n'avons pas besoin d'abstractions de couches supérieures dans socket.io.
Koorosh Pasokhi

Réponses:

3

Mise à jour: Sur la base de ma lecture du code dans uWebSockets.js, des modifications devraient être apportées pour activer tous les paramètres websocket-sharprequis pour activer la compression. Dans Vertx, un serveur Java hautes performances, les paramètres suivants fonctionnent avec la compatibilité Unity websocket-sharppour la compression:

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

Précédemment:

L'erreur est réelle, websocket-sharpne prend en charge quepermessage-deflate , utilisez plutôt DEDICATED_COMPRESSOR( compression: 2).

DocteurPangloss
la source
Malheureusement, la définition de la méthode de compression sur 2 n'a pas modifié le message d'erreur :(
Koorosh Pasokhi
Ensuite, il y a un problème sous-jacent avec uWebSockets. J'utilise les websockets vertx de Java avec compression avec WebSocketSharp dans le client qui a plus de champs de configuration, et cela fonctionne.
DoctorPangloss
Essayez peut-être de créer un test directement dans uWebSockets.js qui reproduit le comportement du WebSocket.csrejet d'en-tête de? DEDICATED_COMPRESSORdevrait vraiment fonctionner!
DoctorPangloss
De quel test parlez-vous? L'architecture de uWebSockets.js est un peu compliquée. Il a trois couches écrites en JS, C ++ et C, ce qui rend le débogage difficile.
Koorosh Pasokhi
Je veux dire créer un test dans le répertoire du projet uWebSockets.js qui reproduit la poignée de main websocket-sharp, que vous pourriez découvrir peut-être en le connectant à un serveur conforme et en voyant ce qui se passe?
DoctorPangloss