How to implement WebSocket in .net for chats?

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

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

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 .