WebSocket in .NET
Real-Time, Two-Way Communication Over TCP/IP
Introduction
There are many techniques for achieving near-real-time communication using HTTP, such as Polling, Long Polling, Server-Sent Events, or even Webhooks.
All approaches share the same issue — they are limited to one-way communication.
In this article, we will explore WebSocket, a protocol built on top of TCP/IP that allows two-way, real-time communication and use it to implement a simple chat app in .NET 8.
Bored of reading? Try my interactive tutorials over at codeinteractive.dev!
WebSocket vs Server Sent Events
Just like in my previous articles, I will compare WebSocket with the closest alternative — SSE.
Similiarities
Both SSE and WebSockets:
- use a single persistent TCP/IP connection
- are supported in all modern browsers
- allow real-time communication
Differences
Server Sent Events work with a simple HTTP request, which is limited by its Request — Response design to one-way communication.
WebSocket is a two-way (full duplex) real-time communication protocol built on top of TCP/IP. It uses an HTTP request to establish a connection (handshake), which is then upgraded to a WebSocket connection.
Unlike SSE, WebSocket also supports binary messages.
This makes it an excellent choice for cases where two-way communication is required, such as a chat app.
Implementing a WebSocket Server
Let’s build a simple WebSocket server in .NET 8.
If you just want the code, you can get it on my GitHub.
First, we will set up a web server and the required dependencies.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ChatService>();
var app = builder.Build();
app.UseWebSockets();
Then, we add a simple endpoint that will check for the required headers to determine if it’s a WebSocket request, and if so, accept it and pass it onto our ChatService.
app.MapGet("/", async (HttpContext context, ChatService chatService) =>
{
if (context.WebSockets.IsWebSocketRequest)
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await chatService.HandleWebSocketConnection(webSocket);
}
else
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Expected a WebSocket request");
}
});
In the ChatService, we will handle the WebSocket connection. It’s a singleton service that keeps track of all connected clients.
public class ChatService
{
private readonly List<WebSocket> _sockets = new();
public async Task HandleWebSocketConnection(WebSocket socket)
{
_sockets.Add(socket);
var buffer = new byte[1024 * 2];
while (socket.State == WebSocketState.Open)
{
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), default);
if (result.MessageType == WebSocketMessageType.Close)
{
await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, default);
break;
}
foreach (var s in _sockets)
{
await s.SendAsync(buffer[..result.Count], WebSocketMessageType.Text, true, default);
}
}
_sockets.Remove(socket);
}
}
While the WebSocket is open, we await the next message. If it’s a Close message, we simply close the connection. If not, we read it and write it into an allocated buffer.
We then take a slice of the buffer that contains the message and send it to all connected clients.
To keep it simple, we don’t validate message type or message size, or serialize/deserialize to domain entities, which is something you would probably want to do in a real application.
Implementing a WebSocket Client
As it’s supported by all browsers, we will use HTML and JavaScript to build a client.
<div id="messages"></div>
<input id="chatbox" >
<script>
const socket = new WebSocket('ws://localhost:5007');
socket.onmessage = function (event) {
const messages = document.getElementById("messages");
messages.innerHTML += `<p>${event.data}</p>`;
};
document.getElementById("chatbox").addEventListener("keyup", function(event) {
if (event.key === "Enter") {
socket.send(event.target.value);
event.target.value = "";
}
});
</script>
This simple code opens up a WebSocket connection and displays something resembling a chat application.
Conclusion
I have introduced you to the WebSocket protocol and shown you how to implement a simple WebSocket server in .NET 8
For a more advanced use case, check out my chat app.
If you’re interested in .NET topics, consider subscribing.
Any thoughts? Comments are welcome!