ASP.NET Core WebSockets
Solution 1
I've implemented a WebSocket server in ASP.NET core using these docs and it worked out quite well for me: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1
The main idea is that after you accept a request with AcceptWebSocketAsync()
you take the returned WebSocket
object and use it to send and receive. Typically you would create a loop, calling ReceiveAsync
until some condition is met (you determine the session is done when you receive a certain message, or the client disconnects, etc). As the docs state, When using a WebSocket, you must keep the middleware pipeline running for the duration of the connection.
So if you're passing that WebSocket
connection off to a background worker to perform send/receive on, you need to keep that pipeline open for the duration of your interactions with that client. Then when you're done you signal to the middleware that the connection is finished and it can unwind.
I suspect not keeping the connection open and looping is your issue. I haven't used MapConnectionHandler
for WebSockets before, so this might not work, but it's possible the above strategy will be helpful to you, or follow the docs with a background worker like so:
app.Use(async (context, next) => {
var socket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(socket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
Solution 2
So, I accomplished my goal. Complete use-case is here: https://github.com/Yucked/Rhapsody/blob/beta/src/Controllers/WebSocketHandler.cs
This approach uses System.IO.Pipelines
. I don't have the old source code as I scrapped it and can't figure out why connections were being dropped before even after keeping the pipeline open but hey it works now!
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapConnectionHandler<WebSocketHandler>("/player/{playerId}");
});
public override async Task OnConnectedAsync(ConnectionContext connection) {
var httpContext = connection.GetHttpContext();
if (!httpContext.WebSockets.IsWebSocketRequest) {
await httpContext.Response.CompleteAsync();
return;
}
await httpContext.WebSockets.AcceptWebSocketAsync()
.ContinueWith(async task => {
var webSocket = await task;
await HandleConnectionAsync(webSocket);
});
}
private async Task HandleConnectionAsync(WebSocket websocket) {
try {
do {
var memory = writer.GetMemory(BUFFER_SIZE);
var receiveResult = await webSocket.ReceiveAsync(memory, CancellationToken.None);
if (!receiveResult.EndOfMessage) {
writer.Advance(receiveResult.Count);
continue;
}
await writer.FlushAsync();
} while (webSocket.State == WebSocketState.Open);
}
catch (Exception exception) {
await writer.CompleteAsync(exception);
}
}
Related videos on Youtube
Comments
-
Yucked almost 2 years
I'm trying to have a WebSocket server up and running on ASP.NET Core. I created an empty web project
dotnet new web
changed theProgram.cs
to:public static void Main(string[] args) { Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .Build() .Run(); }
And
Startup.cs
'sConfigureServices
method to:public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddWebSockets(); }
And
Configure
method to:public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseWebSockets(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapConnectionHandler<WebSocketHandler>("/ws"); }); }
And my
WebSocketHandler
'sOnConnectedAsync
method I've the following:public override async Task OnConnectedAsync(ConnectionContext connection) { var context = connection.GetHttpContext(); var endpoint = $"{connection.RemoteEndPoint}"; if (!context.WebSockets.IsWebSocketRequest) { connection.Abort(); _logger.LogCritical($"Request from {endpoint} endpoint aborted."); return; } var websocket = await context.WebSockets.AcceptWebSocketAsync(); _logger.LogInformation($"WebSocket request from {endpoint} endpoint accepted!"); }
The problem arises when I try to connect to
APP_URL/ws
and each time the server closes the connection as soon as it receives the request. Here are the logs: https://pastebin.com/raw/34yu7thwIf I place a
Task.Delay(-1)
at the end ofOnConnectedAsync
method, it keeps the connection open but drops incoming connections.I have searched MSDocs and haven't been able to find much documentation on how to use
MapConnectionHandler<T>
.Would it be safe for me to have a while loop which receives messages from multiple clients in
OnConnectedAsync
? Is this not the right way to handle websocket connections? IsMapConnectionHandler<T>
transient?I'm really confused and can't figure out it's behavior.
-
Yucked over 4 yearsHey, I tried looping and so forth and it seems
MapConnectionHandler
isn't suitable for it. Building a proper middleware fixed the problem for me. -
Macindows over 3 yearsI get BackgroundSocketProcessor not defined? any library i am missing?
-
Swimburger over 2 yearsHow does one actually pass the socket to the background worker, what's in
BackgroundSocketProcessor.AddSocket
?