我正在使用.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);
}
}
答案 0 :(得分: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
时,控制权将返回原始调用者,直到你等待部分完成,然后下一次调用开始。
检查下图,了解等待和异步的工作原理
如果你只是想要火而忘记而不是尝试这样
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
}
表示不等待任务完成
答案 1 :(得分:1)
要防止编译器警告,请使用以下方法:
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
在这种情况下不是一种选择,就像你自己一样。