异常关闭WebSocket不会返回ReceiveAsync。

6
我在服务器上开设了一个线程,用于处理通过WebSocket连接从客户端接收到的数据:
    public HttpResponseMessage Get()
    {
        if (HttpContext.Current.IsWebSocketRequest)
        {
            HttpContext.Current.AcceptWebSocketRequest(ProcessWebSocket);
        }

        return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
    }

    private async Task ProcessWebSocket(AspNetWebSocketContext context)
    {
        WebSocket webSocket = context.WebSocket;    //Gets the current WebSocket object. 
        const int maxMessageSize = 1024;
        byte[] receivedDataBuffer = new byte[maxMessageSize];
        logger.Debug("WSC-New WebSocket connection");
        while (webSocket.State == WebSocketState.Open)
        {
            var timeOut = new CancellationTokenSource(70000).Token;
            WebSocketReceiveResult webSocketReceiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(receivedDataBuffer), timeOut);
            //If input frame is cancelation frame, send close command. 
            if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, String.Empty, CancellationToken.None);
                logger.Debug("WSC-Close WebSocket connection frame received");
            }
            else
            {
                //Process received data....
            }
        }
        logger.Debug("WSC-WebSocket closed, WebSocket state: {0}", webSocket.State.ToString());
    }

只要另一端正确关闭WebSocket连接,我就会在最后收到记录器信息,表明这个线程正确终止。如果客户端进入休眠状态或程序突然停止,则不会发生正常的WebSocket关闭。在这种情况下,webSocket.ReceiveAsync永远不会返回。我担心如果启动了足够多的这些线程却没有终止,可能会出现严重的内存泄漏问题。超时取消令牌应该在70秒后使其返回,但实际上并未发生。如果我尝试在此情况下从另一个线程访问webSocket对象,则会出现以下异常:无法访问已处理的对象。

我该如何从await webSocket.ReceiveAsync(new ArraySegment(receivedDataBuffer), timeOut); 返回到正确关闭此线程?


尝试使用Keep-Alive超时。请参见:https://github.com/websocket-client/websocket-client/issues/54 - jdweng
1个回答

3

问题:

任何连接突然关闭都会导致另一个套接字无法再接收到数据,这会使您的套接字保持打开状态。

解决方案:

尝试使用Keep-Alive来确定连接何时超时,另一种解决方案是使用一个可以被终止的单独线程来丢弃它,但我不确定这对您是否有效。

开始使用套接字 EX:

Thread X = new Thread(() => {
    Connect();
});
X.Start();

结束套接字 EX:
X.Abort();
// You could also force a Garbage Collection using
GC.Collect();

1
默认情况下,保持连接设置为30秒,但似乎实际上并没有起到作用。我猜测是IIS/IIS Express即使您的网站不再运行,也会维护保持连接。您可以使用线程(或更好的取消令牌)强制关闭套接字,但是您无法知道它是否已经停止发送还是已经消失了。 - trampster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接