Uso de la compresión websocket con uWebSockets.js y Websocket-Sharp

11

Tenemos un juego móvil que usa websocket para las conexiones. El servidor es una aplicación Node.js que usa la biblioteca uWebSockets.js y el cliente es una aplicación Unity que usa la biblioteca Websocket-Sharp . Ambos juegan bien juntos y no hemos encontrado un problema con ellos.

Recientemente queríamos habilitar la compresión websocket . Ambas bibliotecas declararon que son compatibles con la extensión de compresión por mensaje, pero parece que hay algo incompatible con ellas. Porque cuando configuramos para usar la compresión, la conexión websocket se cierra inmediatamente en el protocolo de enlace.

También probamos el cliente con la biblioteca ws y se proporciona un ejemplo para la compresión con el mismo resultado. Intentamos jugar con las opciones de compresión de ws y descubrimos que cuando comentamos la opción serverMaxWindowBits (por defecto es el valor negociado) la conexión podría establecerse y el envío y recepción de mensajes funciona sin problemas. También preguntamos sobre el control de serverMaxWindowBits en uWebsockets.

Lo último que intentamos fue conectar un servidor uWS mínimo y un cliente websocket. Aquí está el código para el servidor:

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

Aquí está el código del cliente:

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

Cuando ejecutamos el servidor y el cliente, el cliente emite el siguiente error:

Error | WebSocket.checkHandshakeResponse | El servidor no ha devuelto 'server_no_context_takeover'. Fatal | WebSocket.doHandshake | Incluye un encabezado Sec-WebSocket-Extensions no válido.

Parecía que el cliente esperaba el encabezado server_no_context_takeover y no recibió uno. Revisamos la fuente de uWebsockets (parte C ++ del módulo uWebsockets.js) y encontramos una condición comentada para devolver el encabezado server_no_context_takeover. Así que descomentamos la condición y creamos uWebsockets.js y probamos nuevamente para encontrar el siguiente error en el cliente:

WebSocketSharp.WebSocketException: el encabezado de un marco no se puede leer desde la secuencia.

¿Alguna sugerencia para hacer que estas dos bibliotecas funcionen juntas?

Arash
fuente
No estoy seguro de que ayude en absoluto, pero socket.io se comprime de forma predeterminada. Sé que el Mejor HTTP 2 tiene bastante buen soporte para socket.io - websockets.
Samuel G
@SamuelG Gracias, pero usar socket.io no es una opción porque actualmente estamos manejando 5k + conexiones simultáneas con recursos mínimos.
Koorosh Pasokhi
@KooroshPasokhi, ¿qué concluyó su razonamiento de que socket.io no podría manejar su carga o requiere muchos recursos? Me encantaría saber más sobre tus exámenes.
Samuel G
@SamuelG Los comentarios no son para debates, pero digamos que las razones principales son que el foco de socket.io no es el rendimiento y no necesitamos abstracciones de capa superior en socket.io.
Koorosh Pasokhi

Respuestas:

3

Actualización: según mi lectura del código uWebSockets.js, se necesitarían hacer cambios para habilitar todos los parámetros que se websocket-sharpdeben configurar para habilitar la compresión. En Vertx, un servidor Java de alto rendimiento, las siguientes configuraciones funcionan con Unity websocket-sharppara compresión:

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

Previamente:

El error es real, websocket-sharpsolo admite permessage-deflate, use DEDICATED_COMPRESSOR( compression: 2) en su lugar.

DoctorPangloss
fuente
Desafortunadamente, configurar el método de compresión en 2 no cambió el mensaje de error :(
Koorosh Pasokhi
Luego hay un problema subyacente con uWebSockets. Utilizo vertx websockets de Java con compresión con WebSocketSharp en el cliente que tiene más campos de configuración, y simplemente funciona.
DoctorPangloss
¿Quizás intente crear una prueba directamente en uWebSockets.js que reproduzca el comportamiento del WebSocket.csrechazo de encabezado? DEDICATED_COMPRESSORrealmente debería funcionar!
DoctorPangloss
¿A qué prueba te refieres? La arquitectura de uWebSockets.js es un poco complicada. Tiene tres capas escritas en JS, C ++ y C, lo que dificulta la depuración.
Koorosh Pasokhi
Me refiero a la creación de una prueba en el directorio del proyecto uWebSockets.js que reproduce el protocolo de enlace web, que podría descubrir al conectarlo a un servidor compatible y ver qué sucede.
DoctorPangloss