Owin.WebSocket icon indicating copy to clipboard operation
Owin.WebSocket copied to clipboard

how to send data to all connected clients or selective clients?

Open chhatraman08 opened this issue 8 years ago • 3 comments

chhatraman08 avatar Nov 21 '17 10:11 chhatraman08

This is my solution to the problem...

[WebSocketRoute("/ws")]
public class MyWebSocket : WebSocketConnection
{
    private static Dictionary<Guid,MyWebSocket> connections = new Dictionary<Guid, MyWebSocket>();

    public Guid UniqueId { get; }

    public MyWebSocket()
    {
        UniqueId = Guid.NewGuid();
    }

    public override void OnOpen()
    {
        connections.Add(UniqueId, this);

        System.Diagnostics.Debug.WriteLine("New websocket connected {0}, now have {1} in list", UniqueId, connections.Count);
    }

    public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription)
    {
        Close(WebSocketCloseStatus.NormalClosure, "");

        connections.Remove(UniqueId);

        System.Diagnostics.Debug.WriteLine("Websocket {0} closed, now have {1} in list", UniqueId, connections.Count);
    }

    public override void OnReceiveError(Exception error)
    {
        Close(WebSocketCloseStatus.ProtocolError, error.Message);

        connections.Remove(UniqueId);

        System.Diagnostics.Debug.WriteLine("Websocket {0} errored with {1}, now have {2} in list", UniqueId, error.Message, connections.Count);
    }

    public override async Task OnMessageReceived(ArraySegment<byte> message, WebSocketMessageType type)
    {
        if (type == WebSocketMessageType.Text)
        {
            var txt = System.Text.Encoding.UTF8.GetString(message.Array, message.Offset, message.Count);
            var output = System.Text.Encoding.UTF8.GetBytes("Received: " + txt);
            await SendText(output, true);
        }
    }

    public static void Broadcast(string text, params object[] args)
    {
        var txtBuffer = System.Text.Encoding.UTF8.GetBytes(string.Format(text, args));
        foreach(var conn in connections.Values)
        {
            conn.SendText(txtBuffer, true);
        }
    }
}

so having that static method I can call MyWebSocket.Broadcast("some event {0}", "0101"); from anywhere...

mika76 avatar Sep 18 '18 16:09 mika76

Hello,

When using this code (Framework 4.7.2) , my websocket client says that it is disconnected by the server just after open. The problem seems to come from connections.Add(UniqueId, this); . Any idea why ?

oschwab avatar Dec 18 '18 10:12 oschwab

@mika76 you have to put this code on a background thread and not within the MyWebSocket class `public static class NotificationDispatcher { private static ConcurrentQueue<byte[]> ActiveQueue { get; set; } = new ConcurrentQueue<byte[]>();

    private static Thread RunningThread;
    private static Dictionary<Guid, NotificationsRoute> _clients = new Dictionary<Guid, NotificationsRoute>();

    public static void ClientAdd(NotificationsRoute route) => _clients.Add(route.UniqueId, route);
    public static void ClientRemove(Guid guid) => _clients.Remove(guid);

    public static void QueueMessage(byte[] message) => ActiveQueue.Enqueue(message);

    public static void StartDispatch(CancellationToken cancellationToken)
    {
        ActiveQueue = new ConcurrentQueue<byte[]>();
        RunningThread = new Thread(() =>
        {
            DateTime lastHeartbeat = DateTime.Now.AddDays(-1);
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    if (ActiveQueue.TryDequeue(out byte[] dispatchMessage) && dispatchMessage.Length > 0)
                        foreach (var conn in _clients.Values)
                        {
                            _ = conn.SendText(dispatchMessage, true);
                        }
                }
                catch (Exception ex)
                {
                    Logger.Log("[Dispatcher Thread Error]" + ex.ToString(), LogLevel.Error);
                }

                //Sample
                if (lastHeartbeat < DateTime.Now.AddSeconds(-10))
                {
                    ActiveQueue.Enqueue($"Heartbeat here at: {lastHeartbeat:yyyy-MM-dd HH:mm:ss}".JsonEncodeSocketMessage());
                    lastHeartbeat = DateTime.Now;
                }

                Thread.Sleep(1000);
            }
        });
        RunningThread.Start();
    }
    public static void StopDispatch()
    {
        try { RunningThread?.Abort(); _clients = null; } catch { }
    }
}`

I am sending a Heartbeat message to all connected clients to see if all clients are getting the message and works great

swagfin avatar Jan 12 '23 10:01 swagfin