Usando a compactação websocket com uWebSockets.js e Websocket-Sharp

11

Temos um jogo para celular usando o websocket para conexões. O servidor é um aplicativo Node.js. usando a biblioteca uWebSockets.js e o cliente é um aplicativo Unity usando a biblioteca Websocket-Sharp . Ambos jogam bem juntos e não encontramos um problema com eles.

Recentemente, queríamos ativar a compactação do websocket . Ambas as bibliotecas declararam que suportam a extensão de compactação por mensagem, mas parece que há algo incompatível com elas. Porque quando configuramos para usar a compactação, a conexão do websocket fecha imediatamente no handshake.

Também testamos o cliente com a biblioteca ws e é fornecido um exemplo de compactação com o mesmo resultado. Tentamos mexer nas opções de compactação ws e descobrimos que, quando comentamos a opção serverMaxWindowBits (o padrão é o valor negociado), a conexão pode ser estabelecida e o envio e recebimento de mensagens funcionam sem problemas. Também perguntamos sobre o controle do serverMaxWindowBits no uWebsockets.

A última coisa que tentamos foi conectar um servidor uWS mínimo e um cliente afiado ao websocket. Aqui está o código para o 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);
        }
    });

Aqui está o código do 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);
      }
    }
  }
}

Quando executamos o servidor e o cliente, o cliente emite o seguinte erro:

Erro | WebSocket.checkHandshakeResponse | O servidor não enviou de volta 'server_no_context_takeover'. Fatal | WebSocket.doHandshake | Inclui um cabeçalho Sec-WebSocket-Extensions inválido.

Parecia que o cliente esperava o cabeçalho server_no_context_takeover e não recebeu um. Revisamos uWebsockets fonte (parte C ++ do módulo uWebsockets.js) e encontrou uma condição comentou para o envio de cabeçalho de volta server_no_context_takeover. Por isso, descomentamos a condição e construímos o uWebsockets.js e testamos novamente para encontrar o seguinte erro no cliente:

WebSocketSharp.WebSocketException: o cabeçalho de um quadro não pode ser lido do fluxo.

Alguma sugestão para que essas duas bibliotecas funcionem juntas?

Arash
fonte
Não tenho certeza se isso ajuda, mas o socket.io é compactado por padrão. Eu sei que o Best HTTP 2 tem um suporte muito bom para socket.io - websockets.
Samuel G
@SamuelG Obrigado, mas usar o socket.io não é uma opção, porque atualmente estamos lidando com 5k + conexões simultâneas com recursos mínimos.
Koorosh Pasokhi
@KooroshPasokhi, o que concluiu seu raciocínio de que o socket.io não seria capaz de lidar com sua carga ou consome muitos recursos? Gostaria de saber mais sobre seus testes.
Samuel G
Os comentários do @SamuelG não são para discussões, mas digamos que os principais motivos são o foco do socket.io não é o desempenho e não precisamos de abstrações de camada mais alta no socket.io.
Koorosh Pasokhi

Respostas:

3

Atualização: com base na minha leitura do código uWebSockets.js, alterações precisariam ser feitas para ativar todos os parâmetros websocket-sharpnecessários para ativar a compactação. No Vertx, um servidor Java de alto desempenho, as seguintes configurações funcionam com o Unity compatível websocket-sharppara compactação:

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

Anteriormente:

O erro é real, websocket-sharpapenas suporta permessage-deflate, use DEDICATED_COMPRESSOR( compression: 2).

DoutorPangloss
fonte
Infelizmente, a definição do método de compactação para 2 não mudou a mensagem de erro :(
Koorosh Pasokhi / 05/19 /
Depois, há um problema subjacente com o uWebSockets. Eu uso os websockets vertx do Java com compactação com o WebSocketSharp no cliente que possui mais campos de configuração e simplesmente funciona.
precisa saber é o seguinte
Talvez tente criar um teste diretamente no uWebSockets.js que reproduza o comportamento da WebSocket.csrejeição de cabeçalho? DEDICATED_COMPRESSORrealmente deve funcionar!
precisa saber é o seguinte
Que teste você quer dizer? A arquitetura do uWebSockets.js é um pouco complicada. Possui três camadas escritas em JS, C ++ e C, o que dificulta a depuração.
Koorosh Pasokhi
Quero dizer, criar um teste no diretório do projeto uWebSockets.js que reproduz o handshake websocket-sharp, que você pode descobrir talvez conectando-o a um servidor compatível e vendo o que acontece?
precisa saber é o seguinte