1

I am trying to establish a websocket connection in a MAUI program in a background thread and output the incoming websocket messages. The websocket server sends correctly. This has already been tested in a console application.

App.xaml.cs class:

namespace MauiWebSocketTest;
public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        WebSocketConnection webSocket = new WebSocketConnection();
        Thread t = new Thread(() => webSocket.OpenWebSocketConnection());
        t.Start();

        MainPage = new AppShell();
    }
}

WebSocketConnection.cs

using Newtonsoft.Json;
using WebSocketSharp;

namespace MauiWebSocketTest
{
    public class WebSocketConnection
    {
        public void OpenWebSocketConnection()
        {
            using (var ws = new     
                    WebSocket("ws://82.165.185.245:8081/deephub/v1/ws/socket"))
            {
                ws.OnOpen += (sender, e) => ws.Send(JsonConvert.SerializeObject(new 
                    FenceEvents()));

                ws.OnMessage += (sender, e) => {
                    Console.WriteLine(e.Data);
                };
            }
        }
    }
}

After starting the program the thread is started but does not return any results. Only the subscribing is executed once but no answers are received. Maybe the thread is killed? I don't have much experience with threads. For any help I would be very grateful

Julian
  • 5,290
  • 1
  • 17
  • 40
DaveR
  • 63
  • 1
  • 10
  • Does that code compile? Where does the `url` come from? You don't seem to pass it to the `WebSocketConnection` class and it's also not a member. Apart from that, I believe that your thread gets collected by the Garbage Collector (GC). That probably happens, because the thread is not actively doing anything. This [thread on the topic](https://stackoverflow.com/a/3707173/4308455) might be useful *(pun intended)*. – Julian Apr 12 '23 at 18:51
  • the url is my ws.//x.x.x.x... adress. I tested the websocket connection in a console application and everyting worked. – DaveR Apr 12 '23 at 18:54
  • The `url` object doesn't show up in your code, it doesn't matter what the actual address is as long as the object exists, since you left it out from the code in your question. Maybe add the code of the working console application to your question for comparison. You might also want to try making the thread a private member of the `App` class, so that you have a reference to the `Thread` object. – Julian Apr 12 '23 at 18:58
  • 1
    As a general question, why do you even need a thread? The WebSocket you're using works with events. What's the purpose of that thread in this context? The [documentation of WebSocketSharp](https://github.com/sta/websocket-sharp) doesn't use threads, either. – Julian Apr 12 '23 at 19:02
  • i updated my post with url. Its exact the same code like in my console application. When i just call the method for Opening the websocketconnection, the first send is successful but i don't receive any messages from the server – DaveR Apr 12 '23 at 19:12
  • 1
    You could've put any dummy URL. Like I mentioned earlier, the actual URL is not of interest, you just shouldn't post code that doesn't compile. Again, **why do you think that you need a thread?** Your (static) console application has a different lifecycle than the constructor of the `App` class. Objects in the `Main()` method of a console app get garbage collected when the console app is closed. The local objects in the `App` constructor get collected as soon as the constructor finishes execution. – Julian Apr 12 '23 at 19:15
  • Because the constant retrieval of messages from the websocket should happen in the background and only when a certain message comes the program should react. – DaveR Apr 12 '23 at 19:18
  • How often do you expect a message to come in? Could you try without the thread, just to see if that works? It would help confirm my theory about the garbage collector. – Julian Apr 12 '23 at 19:24
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/253118/discussion-between-daver-and-ewerspej). – DaveR Apr 12 '23 at 19:25

1 Answers1

4

The problem is that your webSocket and thread objects get garbage collected. You create them in the App constructor. The thread gets created, where you subscribe to the events, but then exits right away.

Since there are no more references to it and it's not doing any work, it gets destroyed right after the constructor finishes execution.

You shouldn't need the thread at all, actually, because WebSocketSharp uses events.

To solve this, make your WebSocketConnection instance a member of the App class and then implement the IDisposable interface in WebSocketConnection.

You won't be able to use the using(var ws = new WebSocket(/*url*/)) code anymore, because the ws object also will get destroyed as soon as the method exists, so with the IDisposable interface implemented following the Dispose pattern, your code should be updated as follows:

using Newtonsoft.Json;
using WebSocketSharp;

namespace MauiWebSocketTest
{
    public class WebSocketConnection : IDisposable
    {
        private WebSocket ws;

        public void OpenWebSocketConnection()
        {
            ws = new WebSocket("ws://82.165.185.245:8081/deephub/v1/ws/socket");
            ws.OnOpen += (sender, e) => ws.Send(JsonConvert.SerializeObject(new 
                FenceEvents()));
            ws.OnMessage += (sender, e) => {
                Console.WriteLine(e.Data);
            };
        }

        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                ws?.Dispose();
            }

            disposed = true;
        }
    }
}

Then use that as follows:

namespace MauiWebSocketTest;
public partial class App : Application
{
    private WebSocketConnection webSocketConnection;

    public App()
    {
        InitializeComponent();

        webSocketConnection = new WebSocketConnection();
        webSocketConnection.OpenWebSocketConnection();

        MainPage = new AppShell();
    }

    ~App()
    {
        //Cleanup unmanaged resources 
        webSocketConnection?.Dispose();
    }
}

Whenever you need to close the connection or otherwise want to reset or destroy the webSocketConnection, the Dispose() method should be called on the current instance.

Julian
  • 5,290
  • 1
  • 17
  • 40
  • Can you add in the 'OnOpen' and 'OnMessage' callbacks to your example for completeness – Andrew Williamson Apr 12 '23 at 19:42
  • Actually, I noticed another problem. The WebSocket will still get destroyed after the `OpenWebSocketConnection()` call finishes, I think. That's where the event handlers live. The `WebSocketConnection` class should probably implement the `IDisposable` interface instead of the `using()` statement. – Julian Apr 12 '23 at 19:45
  • Oh, you're still using the `WebSocketConnection` wrapper class in this solution. Yeah, that class should implement IDisposable. I would move the `WebSocket` instantiation and callback registration to the constructor, and call `_ws.Connect()` in the `OpenWebSocketConnection()` – Andrew Williamson Apr 12 '23 at 19:53
  • @AndrewWilliamson finally i got it. Thank you very much. WIth the IDisposbale Interface implemented and removing using it works now – DaveR Apr 12 '23 at 20:23