Simple SSE Server in .Net Core

artydev - Oct 7 - - Dev Community

Here is a simple example:

using System.Collections.Concurrent;


namespace SillageLogTracer
{
    public class Program
    {
        // Change from ConcurrentBag to ConcurrentDictionary
        private static readonly ConcurrentDictionary<Guid, HttpContext> SseClients = new ConcurrentDictionary<Guid, HttpContext>();

        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddAuthorization();
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthorization();
            app.UseStaticFiles();

            // SSE Endpoint for Trace
            app.MapGet("/sse", async (HttpContext context, CancellationToken token) =>
            {
                context.Response.ContentType = "text/event-stream";

                // Create a unique client ID for this connection
                var clientId = Guid.NewGuid();

                // Add the client to the list of connected clients
                SseClients.TryAdd(clientId, context);

                try
                {
                    while (!token.IsCancellationRequested)
                    {
                        await context.Response.Body.FlushAsync(token);
                        await Task.Delay(1000, token); // Delay for 1 second
                    }
                }
                catch (TaskCanceledException)
                {
                    // Handle the task cancellation gracefully
                }
                finally
                {
                    // Remove the client from the list upon disconnection
                    SseClients.TryRemove(clientId, out _);
                }
            });

            app.MapGet("/sendMessage", async (string message) =>
            {
                // Send message to all connected SSE clients
                var tasks = new List<Task>();

                foreach (var client in SseClients.Values)
                {
                    if (!client.RequestAborted.IsCancellationRequested)
                    {
                        var task = client.Response.WriteAsync($"data: {message}\n\n");
                        tasks.Add(task);
                    }
                }

                // Wait for all messages to be sent
                await Task.WhenAll(tasks);
                return Results.Ok($"Message '{message}' sent to {SseClients.Count} clients.");
            });

            app.Run();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Here is the javascript client :

// client.js
//var message = prompt("Saisissez votre message : ");fetch(`/sendMessage?message=${message}`); 

class TraceClient {
    constructor(url) {
        this.url = url;
        this.eventSource = null;
        this.messageElement = document.getElementById('messages');
    }

    connect() {

        this.eventSource = new EventSource(this.url);

        this.eventSource.onopen = () => {
            console.log('Connection established');
        };

        this.eventSource.onmessage = (event) => {
            this.handleMessage(event.data);
        };

        this.eventSource.onerror = (error) => {

            console.error(error);
        };
    }

    disconnect() {
        if (this.eventSource) {
            this.eventSource.close();
            console.log('Connection closed');
            console.log("closing...")
        }
    }

    handleMessage(data) {

        if (data.type === 'trace') {
            this.addMessage(`Received trace: ${data.content}`);
        } else if (data.type === 'heartbeat') {
            this.addMessage('Heartbeat received');
        } else {
            this.addMessage(data)
        }

    }

    addMessage(message) {

        const msgElement = document.createElement('li');
        msgElement.textContent = message;
        this.messageElement.appendChild(msgElement);
        this.messageElement.scrollTop = this.messageElement.scrollHeight;
    }
}

document.addEventListener('DOMContentLoaded', () => {
    const traceUrl = '/sse';
    const client = new TraceClient(traceUrl);



    client.connect();

});

Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player