websocket-sharp - OnMessage callback is not running in the main thread

798 views Asked by At

I have a WPF (.NET Framework 4.6) application that uses websocket-sharp (version 3.0.0) to create a websocket server. I have a WebsocketServer and using EventHandler to tranfer event to MainWindow.xaml.cs but it not working. The MainWindow.xaml.cs listened to a RaiseOnScanDevice event but not any event invoked here.

I think this issue is relative to different thread. I try using Dispatcher.Invoke but it still not working.

System.Windows.Application.Current.Dispatcher.Invoke(new System.Action(() =>
{
    RaiseOnScanDevice(this, new EventArgs());
}));

I found an issue (https://github.com/sta/websocket-sharp/issues/350) but the answers do not resolve my issue.

Please help me a solution for this issue.

WebsocketServer.cs file

public class WebsocketServer : WebSocketBehavior
{
    private static readonly Lazy<WebsocketServer> lazyInstance = new Lazy<WebsocketServer>(() => new WebsocketServer());

    public static WebsocketServer Instance
    {
        get
        {
            return lazyInstance.Value;
        }
    }

    private const string TAG = "WebsocketServer";
    private const string HOST_IP_ADDRESS = "127.0.0.2"; // localhost
    private const int PORT = 38001;

    public WebSocketServer socket;
    private PacketHandler packetHandler = new PacketHandler();

    public event EventHandler<EventArgs> RaiseOnScanDevice = new EventHandler<EventArgs>((a, e) => { });

    public WebsocketServer()
    {
        Initialize();
    }

    public void Initialize()
    {
        socket = new WebSocketServer(IPAddress.Parse(HOST_IP_ADDRESS), PORT);
        socket.AddWebSocketService<WebsocketServer>("/");
        StartServer();
    }

    public void StartServer()
    {
        socket.Start();
    }

    public void StopServer()
    {
        socket.Stop();
    }

    protected override Task OnOpen()
    {
        return base.OnOpen();
    }

    protected override Task OnClose(CloseEventArgs e)
    {
        return base.OnClose(e);
    }

    protected override Task OnError(ErrorEventArgs e)
    {
        return base.OnError(e);
    }

    protected override Task OnMessage(MessageEventArgs e)
    {
        System.IO.StreamReader reader = new System.IO.StreamReader(e.Data);
        string message = reader.ReadToEnd();
        //Converting the event back to 'eventName' and 'JsonPayload'
        PacketModel packet = packetHandler.OpenPacket(message);
        HandleMessageFromClient(packet);
        return base.OnMessage(e);
    }

    private void HandleMessageFromClient(PacketModel packet) {
        var eventName = packet.EventName;
        var data = packet.Data;

        if (eventName == null || eventName.Equals(""))
        {
            return;
        }

        switch (eventName)
        {
            case SocketEvent.Hello:
                Send("OK");
                break;
            case SocketEvent.ScanDevice:
                ScanDevice();
                break;
            default:
                break;
        }
    }

    private void ScanDevice()
    {
        try
        {
            RaiseOnScanDevice(this, new EventArgs());
            
            // or dispatch to Main Thread
            System.Windows.Application.Current.Dispatcher.Invoke(new System.Action(() =>
            {
                RaiseOnScanDevice(this, new EventArgs());
            }));
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
}

MainWindow.xaml.cs file

public partial class MainWindow : Window
{
    public WebsocketServer WebsocketConnection
    {
        get { return WebsocketServer.Instance; }
    }

    public MainWindow()
    {
        InitializeComponent();
        WebsocketConnection.RaiseOnScanDevice += SocketConnection_RaiseOnScanDevice;           
    }

    private void SocketConnection_RaiseOnScanDevice(object sender, EventArgs e)
    {
        Console.WriteLine("SocketConnection_RaiseOnScanDevice");
    }
1

There are 1 answers

1
Thanh Tâm Trần On

The queue of messages is a good idea but you may want to use a lock to guard access to it. Most likely it won't be an issue but if you don't, you leave yourself open to the possibility of an error if the coroutine is reading from the queue as the websocket is writing to it. For example you could do something like this:

var queueLock = new object();
var queue = new Queue<MyMessageType>();
    
// use this to read from the queue
MyMessageType GetNextMessage() 
    {
       lock (queueLock) {
            if (queue.Count > 0) return queue.Dequeue();
            else  return null;
        }
    }
    
// use this to write to the queue
   void QueueMessage(MyMessageType msg)
    {
        lock(queueLock) {
            queue.Enqueue(msg);
        }
    }