.NET WebSocket没有明显原因关闭

时间:2018-03-08 17:03:38

标签: c# .net tcp websocket

我正在测试当客户端能够足够快地从服务器端处理数据时.NET WebSockets如何工作。为此,我编写了一个将数据连续发送到WebSocket的应用程序,但在接收循环中包含了一个人工延迟。正如预期的那样,一旦TCP窗口和其他缓冲区填满,SendAsync调用开始需要很长时间才能返回。但几分钟后,SendAsync抛出了其中一个异常:

  

System.Net.HttpListenerException:设备无法识别该命令   System.Net.HttpListenerException:由于线程退出或应用程序请求,I / O操作已中止。

奇怪的是,这只发生在某些消息大小和特定时间。当允许客户端读取所有不受限制的数据时,连接是稳定的。此外,当客户端被完全阻止而根本不读取时,连接保持打开状态。

通过Wireshark检查数据流显示,当客户端的TCP窗口耗尽时,服务器正在重置TCP连接。

我试图按照这个答案(reporting)但没有成功。调整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 个答案:

答案 0 :(得分:2)

我不是.NET开发人员,但据我在websocket主题中看到这些问题而且我个人认为,这些可能是原因:

  1. 双方websocket上的超时设置非常短暂。
  2. 客户端/服务器端运行时异常(除了记录,必须检查onErroronClose方法以查明原因)
  3. 互联网或连接失败。 Websocket有时也会进入IDLE模式。您必须在websockets上实现心跳系统以使它们保持活动状态。使用pingpong数据包。
  4. 检查服务器端的最大二进制或文本消息大小。还要设置一些缓冲区以避免在消息太大时出现故障。
  5. 正如您所说,您的错误通常会在一定时间内发生,1和2必须帮助您。再次抱歉,如果我无法为您提供代码,但我在java中遇到了同样的问题,我发现这些是必须设置的设置才能使用websockets。搜索如何在客户端和服务器实现中设置这些,并且在此之后一定要好。

答案 1 :(得分:1)

显然,我正在点击HTTP.SYS低速连接攻击对策,如KB 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;