问题描述
我正在使用.NET的HttpListener
类编写Websocket服务器.
I'm writing a websocket server using .NET's HttpListener
class.
从本质上讲,我有一个HandleListener()
函数,该函数等待客户端连接并将每个客户端转换为HandleClient(WebSocket client)
.所以我目前有:
Essentially, I've got a HandleListener()
function which wait for clients to connect and yield each client to HandleClient(WebSocket client)
. So I currently have:
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()
就会暂停执行.
Problem is, I can't seem to process more then one client. It looks like the execution of HandleListener()
halts as long as the first client is connected.
我尝试从对HandleClient()
的调用中删除await
,但是出现因为未等待此调用..."错误.我可以将HandleClient()
设置为async void
方法,但这不是事件处理程序.
顺便说一句,HandleClient()
是async Task
的原因是因为它一直在循环执行,直到侦听器死掉为止:
I tried removing the await
from the call to HandleClient()
, but I get the "because this call is not awaited..." error. I can make HandleClient()
a async void
method, but this is not an event handler.
BTW, the reason that HandleClient()
is async Task
is because it's doing, all over in a loop until the listener is dead:
recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
据我了解,一劳永逸的方法总体上是不好的,而且我似乎无法通过async-await实现来实现.但是HandleClient()
是一劳永逸的方法,我看不出有其他方法可以实现我所需要的.
From what I understand, a fire-and-forget approach is bad overall, and I can't seem to achieve it with async-await implementation. But HandleClient()
is a fire-and-forget method, and I don't see any other way of achieving what I need.
添加了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);
}
}
推荐答案
为防止编译器警告,请使用如下方法:
To prevent compiler warning, use method like this:
public static class TaskExtensions {
public static void Forget(this Task task) {
}
}
然后就做
HandleClient(webSocket).Forget()
如果走这条路,请确保以某种方式处理HandleClient
内部的所有异常(例如,将整个内容包装到try-catch中).在这种特殊情况下,这种方法没有内在的坏处".
If you go this route, ensure that you handle all exceptions inside HandleClient
somehow (wrap whole thing into try-catch for example). There is nothing inherently "bad" in this approach in this particular case.
替代方法是:
HandleClient(webSocket).ContinueWith(task => {
if (task.IsFaulted && task.Exception != null) {
// handle it here
}
});
如您所见,在这种情况下,
等待HandleClient
并不是一种选择.
awaiting HandleClient
is not an option in this case, as you see yourself.
这篇关于将异步等待与即发即弃的方法混合在一起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!