1

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");
    }
Duc Nguyen
  • 59
  • 6
  • Besides that RaiseOnScanDevice is an odd name for an event, you should never invoke an event without checking it for null. Use the `?.` operator: `RaiseOnScanDevice?.Invoke(this, new EventArgs());`. That said, have you debugged your code to find out whether the ScanDevice method is actually called at all? – Clemens Oct 11 '22 at 07:41
  • @Clemens I tried `RaiseOnScanDevice?.Invoke(this, new EventArgs());` and make sure `RaiseOnScanDevice` not null but it not working – Duc Nguyen Oct 11 '22 at 10:01
  • What does "*not working*" mean? Did you do any debugging? – Clemens Oct 11 '22 at 10:06
  • @Clemens Yes, I have a breakpoint to debug, and `RaiseOnScanDevice ` is not null. But `SocketConnection_RaiseOnScanDevice` function still not receive event from `RaiseOnScanDevice` – Duc Nguyen Oct 11 '22 at 10:15
  • Although the ScanDevice method is called? And it's called on the same object? Your "singleton" implementation does not forbid to create more than one WebsocketServer instance. – Clemens Oct 11 '22 at 10:43
  • @Clemens Sure ScanDevice method is called and on the same object. I tried to add a public get/set property `IsConnected` (default `false`) at WebsocketServer class and set it equal to `true` at MainWindow (`WebsocketConnection.IsConnected = true;`). But when I debug at the `OnMessage` function, `IsConnected` is still equal to `false`. I don't understand what happened :(( – Duc Nguyen Oct 11 '22 at 11:02
  • Sounds like there is more than one instance. Make the WebsocketServer constructor private. – Clemens Oct 11 '22 at 11:08
  • @Clemens I can not set WebsocketServer constructor private because this class must have public to config WebsocketServerSharp, this line `socket.AddWebSocketService("/");`. I tried to remove the singleton and use the new WebsocketServer() syntax, but can not resolve issue – Duc Nguyen Oct 11 '22 at 11:18
  • Well, then you've got the source of your problem. The WebSocketServer class creates another WebsocketServer instance (consider a better name). Set up your singleton so that the constructor initializes the static Instance property (drop the Lazy stuff). – Clemens Oct 11 '22 at 11:29

1 Answers1

1

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);
        }
    }
  • Or just use [ConcurrentQueue](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=net-6.0). – Clemens Oct 11 '22 at 08:20