I have a Asp.NET core application. Startup.Configure()
mainly contains this code
app.UseWebSockets();
app.Use(async (httpContext, next) =>
{
// If the request is a WebServerRequest, handle it as such ...
if (httpContext.WebSockets.IsWebSocketRequest)
{
ClientHandler h = new ClientHandler(httpContext);
if (h.IsWebsockOpen)
{
await h.Handle();
}
else
{
httpContext.Response.StatusCode = 400;
}
}
// ... otherwise just hand the request to the next element in chain
else
{
await next();
}
});
Inside h.Handle()
the client is supposed to register with a ClientManager
which in turn multicasts that a new client has connected like this
public async Task Multicast<T>(List<ClientHandler> l, Msg<T> m)
{
foreach (ClientHandler h in l)
{
if (h.IsWebsockOpen)
{
await h.webSocket.SendAsync(
System.Text.Encoding.UTF8.GetBytes(m.ToString()),
System.Net.WebSockets.WebSocketMessageType.Text,
true,
System.Threading.CancellationToken.None);
}
}
}
I am now wondering if that is safe to do. I can imagine a scenario where two clients connect at the same time resulting in h.webSocket.SendAsync
being called twice simultaneously, which is not allowed as said in
Remarks This operation will not block. The returned Task object will complete after the data has been sent on the WebSocket.
Exactly one send and one receive is supported on each WebSocket object in parallel.
Wraping the h.webSocket.SendAsync
in a lock
-statement seems to be impossible due to the await
keyword.
How can I make my code safe? Related questions are either not using WebSockets or they use different frameworks for which mechanisms apply.
答案 0 :(得分:1)
您可以在此处使用信号量,特别是SemaphoreSlim
。我建议在您的gifski
类上创建一个SendAsync
方法,并通过该方法传递所有请求-即从您的ClientHandler
方法进行调用。
您的Multicast
的内容将类似于:
ClientHandler
class ClientHandler() {
private readonly SemaphoreSlim _sendLock;
public ClientHandler(HttpContext context) {
_sendLock = new SemaphoreSlim(1, 1);
//....
}
public async Task SendAsync(string msg) {
await _sendLock.WaitAsync();
try {
await webSocket.SendAsync(
System.Text.Encoding.UTF8.GetBytes(msg.ToString()),
System.Net.WebSockets.WebSocketMessageType.Text,
true,
System.Threading.CancellationToken.None);
} finally {
_sendLock.Release();
}
}
}
是SemaphoreSlim
,因此您需要注意这一点,它的IDisposable
方法有一些重载的取消令牌和/或超时,可能适合您使用。