How to implement WebSocket in .net for chats?

By Anubhav Kumar — Published: 17-Dec-2025 • Last updated: 18-Dec-2025 76

You should not implement raw WebSocket handlers yourself unless you have a very specific reason.
In real-world systems, SignalR (classic for .NET Framework / SignalR Core for .NET Core) is the industry-standard abstraction over WebSockets.

Since you are on ASP.NET MVC 5 / .NET Framework 4.8, this answer is SignalR (not ASP.NET Core) and battle-tested for production.

Why NOT raw WebSockets in production?

Raw WebSockets mean you must implement everything yourself:

  • Connection lifecycle
  • Reconnect logic
  • Heartbeats
  • Scaling across servers
  • Fallback transports
  • Authentication & authorization
  • Group management
  • Message routing

SignalR already solves all of this and uses WebSockets internally when available.

Architecture (Production Chat)

Browser
  |
SignalR JS Client
  |
SignalR Hub
  |
ChatService
  |
Database / Cache (SQL + Redis)

Step 1: Install SignalR (ASP.NET MVC 5)

Install-Package Microsoft.AspNet.SignalR

Step 2: Enable SignalR (OWIN Startup)

Create Startup.cs

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(YourApp.Startup))]
namespace YourApp
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

Step 3: Create Chat Hub (Server)

using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;

public class ChatHub : Hub
{
    public override Task OnConnected()
    {
        string userId = Context.User.Identity.Name;
        Groups.Add(Context.ConnectionId, userId);
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        string userId = Context.User.Identity.Name;
        Groups.Remove(Context.ConnectionId, userId);
        return base.OnDisconnected(stopCalled);
    }

    // 1-to-1 chat
    public Task SendPrivate(string toUserId, string message)
    {
        string fromUser = Context.User.Identity.Name;

        return Clients.Group(toUserId).receiveMessage(fromUser, message);
    }

    // Group chat
    public Task SendGroup(string groupName, string message)
    {
        string fromUser = Context.User.Identity.Name;
        return Clients.Group(groupName).receiveGroupMessage(fromUser, message);
    }
}

Step 4: Secure Hub (IMPORTANT)

SignalR automatically integrates with ASP.NET authentication.

[Authorize]
public class ChatHub : Hub
{
}

If you use Forms Authentication or ASP.NET Identity → works out of the box.

Step 5: JavaScript Client (Browser)

<script src="/Scripts/jquery.signalR-2.4.3.min.js"></script>
<script src="/signalr/hubs"></script>

<script>
    var chat = $.connection.chatHub;

    chat.client.receiveMessage = function (from, message) {
        console.log(from + ": " + message);
    };

    $.connection.hub.start().done(function () {
        console.log("Connected");

        $('#send').click(function () {
            chat.server.sendPrivate("user2", "Hello!");
        });
    });
</script>

Step 6: Message Persistence (Production MUST)

Never rely on in-memory messages.

public class ChatService
{
    public void SaveMessage(string from, string to, string text)
    {
        // Save to SQL / NoSQL
    }
}

Call this inside the Hub before sending.

Step 7: Scaling (CRITICAL for Production)

Problem

  • SignalR hubs are in-memory per server.

Solution: Redis Backplane

Install-Package Microsoft.AspNet.SignalR.Redis
public void Configuration(IAppBuilder app)
{
    GlobalHost.DependencyResolver
        .UseRedis("localhost", 6379, "", "ChatApp");

    app.MapSignalR();
}

This allows:

  • Multiple IIS servers
  • Load balancer support
  • Horizontal scaling

Step 8: Reliability Features

Feature Status
Reconnect Built-in
Fallback (LongPolling) Built-in
Heartbeat Built-in
Transport negotiation Built-in
Thread safety Built-in

Step 9: Message Delivery Guarantee

For enterprise-grade chat:

  • Save message first
  • Send notification second
  • Use delivery status
public class ChatMessage
{
    public long Id;
    public string From;
    public string To;
    public string Text;
    public byte Status; // Sent, Delivered, Read
}

Step 10: When to use RAW WebSocket?

Only if:

  • You are building a game server
  • You need binary streaming
  • Ultra-low latency trading system
  • Otherwise → SignalR is correct.
Anubhav Kumar
Anubhav Kumar
Student

LinkedIn

The Anubhav portal was launched in March 2015 at the behest of the Hon'ble Prime Minister for retiring government officials to leave a record of their experiences while in Govt service .