Закрытие.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, и, по моему мнению, это могут быть причины:
- Очень короткое время ожидания на веб-сокете с обеих сторон.
- Исключения времени выполнения на стороне клиента / сервера (кроме регистрации, необходимо проверить
onError
а такжеonClose
Способы понять почему) - Интернет или сбои подключения. Websocket иногда тоже переходит в режим IDLE. Вы должны внедрить систему сердцебиения на веб-сокеты, чтобы поддерживать их работу. использование
ping
а такжеpong
пакеты. - проверьте максимальный размер двоичного или текстового сообщения на стороне сервера. Также установите несколько буферов, чтобы избежать сбоя, когда сообщение слишком большое.
Как вы сказали, ваша ошибка обычно происходит в течение определенного времени, 1 и 2 должны вам помочь. Еще раз извините, если я не могу предоставить вам коды, но у меня были те же проблемы в Java, и я обнаружил, что это настройки, которые должны быть установлены для работы с веб-сокетами. Ищите, как установить их в ваших клиентских и серверных реализациях, и вы должны быть в порядке после этого.