繁体   English   中英

将异步等待与即发即弃方法混合在一起

[英]Mixing async-await with fire-and-forget approach

我正在使用.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()的执行就HandleListener()暂停。

我尝试从对HandleClient()的调用中删除await ,但是出现“因为未等待此调用...”错误。 我可以使HandleClient()成为async void方法,但这不是事件处理程序。
顺便说一句, HandleClient()async Task的原因是因为它一直在循环执行,直到侦听器死机为止:

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

据我了解,一劳永逸的做法总体上是不好的,而且我似乎无法通过async-await实现来实现。 但是HandleClient() 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);
        }
    }

之所以会这样,是因为您为此编写了代码,我的意思是说您编写了如下方法。

   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控件时,控制权将返回给原始呼叫者,直到您等待的部分完成并在此之后开始下一次调用。

检查下图,这是如何等待和异步的

在此处输入图片说明

如果你只是想生火而忘了尝试

   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
    }

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

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

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
    }
});

如您所见,在这种情况下,等待HandleClientHandleClient

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM