Использование сжатия websocket с uWebSockets.js и Websocket-Sharp

11

У нас есть мобильная игра, использующая веб-сокет для соединений. Сервер представляет собой приложение Node.js, использующее библиотеку uWebSockets.js, а клиент - приложение Unity, использующее библиотеку Websocket-Sharp . Они оба хорошо играют вместе, и мы не столкнулись с проблемой с ними.

Недавно мы хотели включить сжатие websocket . Обе библиотеки заявили, что они поддерживают расширение сжатия сообщений, но, похоже, что-то несовместимо с ними. Потому что, когда мы настраиваем использовать сжатие, соединение веб-сокета немедленно закрывается при рукопожатии.

Мы также проверили клиент с библиотекой ws и предоставили пример для сжатия с тем же результатом. Мы попытались изменить параметры сжатия ws и обнаружили, что когда мы комментируем параметр serverMaxWindowBits (по умолчанию это согласованное значение), соединение может быть установлено, и отправка и получение сообщений работают без проблем. Мы также спросили об управлении serverMaxWindowBits в uWebsockets.

Последнее, что мы попробовали, - это подключение минимального сервера uWS и клиента с поддержкой веб-сокетов. Вот код для сервера:

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

Вот код клиента:

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

Когда мы запустили сервер и клиент, клиент выдаст следующую ошибку:

Ошибка | WebSocket.checkHandshakeResponse | Сервер не отправил обратно «server_no_context_takeover». Fatal | WebSocket.doHandshake | Содержит недопустимый заголовок Sec-WebSocket-Extensions.

Казалось, что клиент ожидал заголовок server_no_context_takeover и не получил его. Мы рассмотрели источник uWebsockets (часть C ++ модуля uWebsockets.js) и обнаружили закомментированное условие для отправки заголовка server_no_context_takeover. Поэтому мы раскомментировали условие и создали файл uWebsockets.js и снова протестировали его на наличие следующей ошибки в клиенте:

WebSocketSharp.WebSocketException: заголовок фрейма не может быть прочитан из потока.

Любые предложения для совместной работы этих двух библиотек?

Сыпь
источник
Не уверен, что это поможет, но socket.io сжимает по умолчанию. Я знаю, что Best HTTP 2 имеет довольно хорошую поддержку для socket.io - websockets.
Самуил Г
@SamuelG Спасибо, но использование socket.io не вариант, потому что в настоящее время мы обрабатываем 5k + одновременных подключений с минимальными ресурсами.
Коорош Пасохи
@KooroshPasokhi, почему вы пришли к выводу, что socket.io не сможет справиться с вашей нагрузкой или требует много ресурсов? Хотелось бы услышать больше о ваших тестах.
Самуил Г
@SamuelG Комментарии не для обсуждений, но, скажем, основные причины в том, что socket.io фокусируется не на производительности, и нам не нужны абстракции более высокого уровня в socket.io.
Коорош Пасохи

Ответы:

3

Обновление: основываясь на моем чтении кода uWebSockets.js, необходимо внести изменения, чтобы включить все параметры, websocket-sharpнеобходимые для включения сжатия. В высокопроизводительном Java-сервере Vertx следующие параметры работают с Unity-совместимым websocket-sharpдля сжатия:

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

Ранее:

Ошибка реальна, websocket-sharpтолько поддерживает permessage-deflate, используйте вместо нее DEDICATED_COMPRESSOR( compression: 2).

DoctorPangloss
источник
К сожалению, установка метода сжатия на 2 не изменила сообщение об ошибке :(
Koorosh Pasokhi
Тогда есть некоторая основная проблема с uWebSockets. Я использую веб-сокеты Java vertx со сжатием с помощью WebSocketSharp в клиенте, который имеет больше полей конфигурации, и он просто работает.
DoctorPangloss
Может быть, попробуйте создать тест непосредственно в uWebSockets.js, который воспроизводит поведение WebSocket.csотклонения заголовка в России? DEDICATED_COMPRESSORдействительно должно работать!
DoctorPangloss
Какой тест вы имеете в виду? Архитектура uWebSockets.js немного сложна. Он состоит из трех слоев, написанных на JS, C ++ и C, что затрудняет отладку.
Коорош Пасохи
Я имею в виду создание теста в каталоге проекта uWebSockets.js, который воспроизводит рукопожатие websocket-sharp, что вы можете обнаружить, подключив его к совместимому серверу и посмотрев, что произойдет?
DoctorPangloss