Hello Everyone,
Hope you are doing well. Today we will delve into one of the communication protocols.
WebSocket provides a way to create a persistent, full-duplex communication channel over a single TCP connection, allowing data to flow in both directions simultaneously. This makes it especially useful for real-time, interactive applications such as live chat systems, gaming, and real-time financial data services.
In this article, we'll explore how to create a real-time chat application using ASP.NET Core for the server-side logic and static HTML for the client-side interface. This combination provides a powerful and efficient method for implementing bi-directional communication between a client and a server.
WebSockets provide a way to open interactive communication sessions between the user's browser and a server. With WebSockets, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.
Overview
Here’s what we’ll cover,
- Setting up the Server: Using ASP.NET Core to handle WebSocket requests and manage connections.
- Client-Side Implementation: Creating a simple HTML interface to send and receive messages.
- Server-Client Interaction: How messages are exchanged and displayed in real-time.
- Testing an application: Demonstrate the working application.
- Advantage Vs Disadvantage: WebSockets provide a unique capability for real-time, full-duplex communication on the web, which can be essential for certain types of applications. However, like any technology, they come with both advantages and disadvantages.
Server-Side Setup with ASP.NET Core
Implementation Details
- ASP.NET Core Setup: Begin by setting up an ASP.NET Core project with the necessary services and middleware for hosting a WebSocket server.
- WebSocket Management: Handle WebSocket requests, establish connections, and manage ongoing communications.
Code Explanation
- Initialize Core Services
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); - Middleware Configuration
if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseWebSockets(); // Dictionary to store WebSocket connections ConcurrentDictionary<string, WebSocket> webSockets = new ConcurrentDictionary<string, WebSocket>(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); - Request Handling
app.Use(async (context, next) => { if (context.WebSockets.IsWebSocketRequest) { WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); var connectionId = Guid.NewGuid().ToString(); webSockets.TryAdd(connectionId, webSocket); await HandleWebSocketCommunication(context, webSocket, connectionId); webSockets.TryRemove(connectionId, out var _); } else { await next.Invoke(); } }); app.Run(); - WebSocket Communication Handling
async Task HandleWebSocketCommunication(HttpContext context, WebSocket webSocket, string connectionId) { var buffer = new byte[1024 * 4]; WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); while (!result.CloseStatus.HasValue) { var receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count); await Task.Delay(3000); // Simulate processing delay var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); var responseMessage = $"[{timestamp}] {connectionId}: {receivedMessage}"; var responseBuffer = Encoding.UTF8.GetBytes(responseMessage); await webSocket.SendAsync( new ArraySegment<byte>(responseBuffer, 0, responseBuffer.Length), WebSocketMessageType.Text, true, CancellationToken.None ); result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); } await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); }
This function handles the lifecycle of a WebSocket connection including receiving data from the client, processing this data (potentially adding a timestamp), and sending a response back.
Client-Side Implementation
The client-side of the application uses basic HTML and JavaScript to establish WebSocket connections and interact with the server.
HTML Setup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Chat Example</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 10px;
}
#messageArea {
height: 300px;
width: 300px;
overflow-y: scroll;
border: 1px solid #ccc;
padding: 5px;
}
#inputArea {
margin-top: 10px;
}
</style>
</head>
<body>
<h2>WebSocket Chat Example</h2>
<div id="messageArea"></div>
<div id="inputArea">
<input type="text" id="messageInput" placeholder="Type a message...">
<button onclick="sendMessage();">Send</button>
</div>
<script>
let websocket;
function connectWebSocket() {
const protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:';
const port = window.location.port ? ':' + window.location.port : '';
const wsUrl = `${protocolPrefix}//${window.location.hostname}${port}/ws`;
websocket = new WebSocket(wsUrl);
websocket.onopen = function () {
displayMessage('[System] Connected to WebSocket server.');
};
websocket.onmessage = function (evt) {
const timestamp = formatTimestamp(new Date());
displayMessage(`[${timestamp}] ${evt.data}`);
};
websocket.onerror = function (evt) {
displayMessage('[System] WebSocket error: ' + evt.data);
};
websocket.onclose = function () {
displayMessage('[System] WebSocket connection closed.');
};
}
function sendMessage() {
const messageInput = document.getElementById('messageInput');
const message = messageInput.value;
const timestamp = formatTimestamp(new Date());
displayMessage(`[${timestamp}] You: ${message}`);
websocket.send(message);
messageInput.value = '';
}
function displayMessage(message) {
const messageArea = document.getElementById('messageArea');
messageArea.innerHTML += message + '<br>';
messageArea.scrollTop = messageArea.scrollHeight;
}
function formatTimestamp(date) {
return date;
}
window.onload = connectWebSocket;
</script>
</body>
</html>

Tuhin PaulPosted Jan 26, 2025, 5:49 PM
You mentioned that managing large-scale deployments can be complex due to persistent connections. What strategies would you recommend for scaling WebSocket applications, such as load balancing, clustering, or using cloud services like Azure SignalR Service? Are there any specific considerations or best practices for handling thousands of concurrent WebSocket connections?
Tuhin PaulPosted Jan 26, 2025, 5:48 PM
Thank you, Ayush Gupta, for this detailed and informative article on building a real-time chat application with (link unavailable) Core and WebSockets. Your step-by-step guide and code examples make it easy to understand and implement WebSockets in a .NET Core application.