0

我正在使用 .NET 的HttpListener类编写一个 websocket 服务器。

本质上,我有一个HandleListener()等待客户端连接并将每个客户端生成到HandleClient(WebSocket client). 所以我目前有:

    private async void HandleListener()
    {
        try
        {
            while (listener != null && listener.IsListening)
            {
                HttpListenerContext listenerContext = await listener.GetContextAsync();
                WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
                WebSocket webSocket = webSocketContext.WebSocket;
                clients.Add(webSocket);
                await HandleClient(webSocket);
            }
        }
        catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
    }

private async Task HandleClient(WebSocket client) { ... }

问题是,我似乎无法处理一个以上的客户。HandleListener()只要连接了第一个客户端,就好像执行暂停。

我尝试await从对 的调用中删除HandleClient(),但我收到“因为不等待此调用...”错误。我可以制作HandleClient()一个async void方法,但这不是一个事件处理程序。
顺便说一句,这HandleClient()async Task因为它在循环中一直在做,直到侦听器死了:

recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);

据我了解,一劳永逸的方法总体上是不好的,我似乎无法通过 async-await 实现来实现它。但这HandleClient() 一种即发即弃的方法,我看不到任何其他方法可以实现我所需要的。


编辑:添加了当前的实现HandleClient()

   private async Task HandleClient(WebSocket client)
    {
        try
        {
            ArraySegment<byte> recievedBuffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]);
            while (listener != null && listener.IsListening && client.State == WebSocketState.Open)
            {
                WebSocketReceiveResult recieveResult;
                using (var ms = new MemoryStream())
                {
                    do
                    {
                        recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
                        ms.Write(recievedBuffer.Array, recievedBuffer.Offset, recieveResult.Count);
                    }
                    while (!recieveResult.EndOfMessage);
                    switch (recieveResult.MessageType)
                    {
                        case WebSocketMessageType.Close:
                            RemoveClient(client, WebSocketCloseStatus.NormalClosure, string.Empty);
                            break;
                        case WebSocketMessageType.Binary:
                            RemoveClient(client, WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame");
                            break;
                        case WebSocketMessageType.Text:
                            OnRecieve?.Invoke(client, System.Text.Encoding.UTF8.GetString(ms.ToArray()));
                            break;
                    }
                }
            }
        }
        catch (WebSocketException ex)
        {
            RemoveClient(client, WebSocketCloseStatus.InternalServerError, ex.Message);
        }
    }
4

2 回答 2

3

为防止编译器警告,请使用如下方法:

public static class TaskExtensions {
    public static void Forget(this Task task) {

    }
}

然后就做

HandleClient(webSocket).Forget()

如果您走这条路,请确保您HandleClient以某种方式处理内部的所有异常(例如,将整个事情包装到 try-catch 中)。在这种特殊情况下,这种方法本身并没有什么“坏处”。

替代方法是:

HandleClient(webSocket).ContinueWith(task => {
    if (task.IsFaulted && task.Exception != null) {
        // handle it here
    }
});

HandleClient如您所见,在这种情况下等待不是一种选择。

于 2017-11-30T10:00:17.100 回答
1

它会这样做,因为您为它编写了代码,我的意思是说您编写了如下方法。

   private async void HandleListener()
    {
        try
        {
            while (listener != null && listener.IsListening)
            {
                HttpListenerContext listenerContext = await listener.GetContextAsync();
                WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
                WebSocket webSocket = webSocketContext.WebSocket;
                clients.Add(webSocket);
                await HandleClient(webSocket);
            }
        }
        catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
    }

在此方法中,当它遇到await控制权时,将返回给原始调用者,直到您等待部分完成并在其后开始下一次调用。

检查下图,了解 await 和 async 的工作原理

在此处输入图像描述

如果你只想着火而忘记,那就试试这样

   private void HandleListener()
    {
        try
        {
            while (listener != null && listener.IsListening)
            {
                HttpListenerContext listenerContext = await listener.GetContextAsync();
                WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
                WebSocket webSocket = webSocketContext.WebSocket;
                clients.Add(webSocket);
                HandleClient(webSocket);
            }
        }
        catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
    }

这意味着不要等待任务完成

于 2017-11-30T09:19:48.727 回答