Закрытие.NET WebSocket без видимой причины

Я тестирую, как работают.NET WebSockets, когда клиент не может обработать данные со стороны сервера достаточно быстро. Для этого я написал приложение, которое непрерывно отправляет данные в WebSocket, но включает искусственную задержку в цикле приема. Как и ожидалось, после заполнения окна TCP и других буферов вызовы SendAsync начинают возвращаться долго. Но через несколько минут SendAsync выдает одно из этих исключений:

System.Net.HttpListenerException: устройство не распознает команду System.Net.HttpListenerException: операция ввода-вывода была прервана из-за выхода из потока или запроса приложения.

Странно то, что это происходит только с определенными размерами сообщений и определенным временем. Когда клиенту разрешено читать все данные без ограничений, соединение стабильно. Кроме того, когда клиент полностью заблокирован и вообще не читает, соединение остается открытым.

Изучение потока данных через Wireshark показало, что именно сервер сбрасывает TCP-соединение, пока окно TCP клиента исчерпано.

Я пытался следовать этому ответу ( принудительно закрывать.NET WebSockets, несмотря на сохранение активности и активность в соединении), но безуспешно. Настройка интервала поддержки WebSocket не имеет никакого эффекта. Кроме того, я знаю, что конечное приложение должно уметь корректно обрабатывать неожиданные отключения, но я не хочу, чтобы они происходили, если их можно избежать.

Кто-нибудь сталкивался с этим? Есть ли тайм-аут, который я могу сделать? Выполнение этого должно привести к ошибке от минуты до полутора-трех минут:

class Program
{
    static void Main(string[] args)
    {
        System.Net.ServicePointManager.MaxServicePointIdleTime = Int32.MaxValue; // has no effect

        HttpListener httpListener = new HttpListener();
        httpListener.Prefixes.Add("http://*/ws/");
        Listen(httpListener);

        Thread.Sleep(500);
        Receive("ws://localhost/ws/");

        Console.WriteLine("running...");
        Console.ReadKey();
    }

    private static async void Listen(HttpListener listener)
    {
        listener.Start();
        while (true)
        {
            HttpListenerContext ctx = await listener.GetContextAsync();

            if (!ctx.Request.IsWebSocketRequest)
            {
                ctx.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
                ctx.Response.Close();
                return;
            }

            Send(ctx);
        }
    }

    private static async void Send(HttpListenerContext ctx)
    {
        TimeSpan keepAliveInterval = TimeSpan.FromSeconds(5); // tweaking has no effect
        HttpListenerWebSocketContext wsCtx = await ctx.AcceptWebSocketAsync(null, keepAliveInterval);
        WebSocket webSocket = wsCtx.WebSocket;

        byte[] buffer = new byte[100];
        while (true)
        {
            await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, CancellationToken.None);
        }
    }

    private static async void Receive(string serverAddress)
    {
        ClientWebSocket webSocket = new ClientWebSocket();
        webSocket.Options.KeepAliveInterval = TimeSpan.FromSeconds(5); // tweaking has no effect

        await webSocket.ConnectAsync(new Uri(serverAddress), CancellationToken.None);

        byte[] receiveBuffer = new byte[10000];
        while (true)
        {
            await Task.Delay(10); // simulate a slow client

            var message = await webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
            if (message.CloseStatus.HasValue)
                break;
        }
}

2 ответа

Решение

По-видимому, я сталкивался с контрмерой атаки низкоскоростного соединения HTTP.SYS, как примерно описано в КБ 3137046 ( https://support.microsoft.com/en-us/help/3137046/http-sys-forcibly-disconnects-http-bindings-for-wcf-self-hosted-servic):

По умолчанию Http.sys рассматривает любую скорость передачи данных менее 150 байт в секунду как потенциальную низкоскоростную атаку и сбрасывает TCP-соединение для освобождения ресурса.

Когда HTTP.SYS делает это, в журнале появляется запись трассировки в%windir%\System32\LogFiles\HTTPERR

Выключить его было просто из кода:

httpListener.TimeoutManager.MinSendBytesPerSecond = UInt32.MaxValue;

Я не.NET-разработчик, но, поскольку я видел подобные проблемы в теме websocket, и, по моему мнению, это могут быть причины:

  1. Очень короткое время ожидания на веб-сокете с обеих сторон.
  2. Исключения времени выполнения на стороне клиента / сервера (кроме регистрации, необходимо проверить onError а также onClose Способы понять почему)
  3. Интернет или сбои подключения. Websocket иногда тоже переходит в режим IDLE. Вы должны внедрить систему сердцебиения на веб-сокеты, чтобы поддерживать их работу. использование ping а также pong пакеты.
  4. проверьте максимальный размер двоичного или текстового сообщения на стороне сервера. Также установите несколько буферов, чтобы избежать сбоя, когда сообщение слишком большое.

Как вы сказали, ваша ошибка обычно происходит в течение определенного времени, 1 и 2 должны вам помочь. Еще раз извините, если я не могу предоставить вам коды, но у меня были те же проблемы в Java, и я обнаружил, что это настройки, которые должны быть установлены для работы с веб-сокетами. Ищите, как установить их в ваших клиентских и серверных реализациях, и вы должны быть в порядке после этого.

Другие вопросы по тегам