Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question WIN10 Build Crashing if I use UdpSocket.BeginReceive or UdpSocket.Receive in a Thread

Discussion in 'Scripting' started by Ascanio1980, Nov 6, 2021.

  1. Ascanio1980

    Ascanio1980

    Joined:
    Jun 7, 2019
    Posts:
    51
    And when I thought running on iOS made me miserable, I realized running on Windows 10 is plain dreadful.

    Ok, so I am trying to listen for incoming UDP messages, but am experiencing application crashes.

    After manually opening up UDP ports in Windows Defender, I tried the following:

    1) UdpClient.BeginReceive + AsyncCallback

    Code (CSharp):
    1. private void StartListening()
    2.         {
    3.             try
    4.             {
    5.                 receivedCommandBytes = new byte[0];
    6.                 receivingCommandsUdpClient = new UdpClient(COMMAND_PORT);
    7.                 receivingCommandsUdpClient.BeginReceive(new AsyncCallback(HandleCommandMessageReceived), null);
    8.             }
    9.             catch (Exception e)
    10.             {
    11.                 Terminal.Trace(e.ToString(), gameObject, Terminal.ERROR);
    12.             }
    13.         }
    14.  
    15. public void HandleCommandMessageReceived(IAsyncResult result)
    16.         {
    17.             try
    18.             {
    19.                 receivedCommandBytes = receivingCommandsUdpClient.EndReceive(result, ref senderEndPoint);
    20.                 receivedCommandString = Encoding.ASCII.GetString(receivedCommandBytes);
    21.  
    22.                 Terminal.Trace($"HandleCommandMessageReceived message: {receivedCommandString}", gameObject);
    23.  
    24.                 OnReceivedCommand?.Invoke(receivedCommandString);
    25.  
    26.                 receivingCommandsUdpClient.BeginReceive(new AsyncCallback(HandleCommandMessageReceived), null);
    27.             }
    28.             catch (Exception e)
    29.             {
    30.                 Terminal.Trace($"HandleMessageReceived - {e}", gameObject, Terminal.ERROR);
    31.             }
    32.         }
    33.  
    When running this, I see the output of Terminal.Trace(), which basically prints out the message on a UI text field on camera, but immediately after that the app crashes.
    I tried commenting the event invoke, to make sure no event handler was causing the crash, and the .BeginReceive() call to start listening again... still crashing.

    2) Thread

    Code (CSharp):
    1. private void StartListening()
    2.         {
    3.             try
    4.             {
    5.                 receivedCommandBytes = new byte[0];
    6.                 receivingCommandsUdpClient = new UdpClient(COMMAND_PORT);
    7.                 commandListenerThread = new Thread(new ThreadStart(ThreadedCommandListening));
    8.                 commandListenerThread.IsBackground = true;
    9.                 commandListenerThread.Start();
    10.             }
    11.             catch (Exception e)
    12.             {
    13.                 Terminal.Trace(e.ToString(), gameObject, Terminal.ERROR);
    14.             }
    15.         }
    16.  
    17. private void ThreadedCommandListening()
    18.         {
    19.             while(true)
    20.             {
    21.                 Terminal.Trace("Thread is cycling...");
    22.                 Thread.Sleep(1000);
    23.                 // Didn't even try to get to the .Receive() part... it crashes already. Gotta love Windows OMG.
    24.             }
    25.         }
    26.  
    Tried looking at Windows Reliability Problems, under Control Panel. It is picking up the frequent crashes, but the crash report doesn't give any useful, actionable information to me.

    I am going to start a third way, using the Update() loop function to check if UdpClient.Available > 0 and UdpClient.Receive(), though I hate the idea of using the Update() loop for something like this, it just feels wrong.

    OFFTOPIC:

    BTW, if I try playing the app in the Editor (on Mac), I get a Socket error for address already in use, so I can't test this quickly in the Editor before compiling a build, transferring it to the Windows machine and launching it (where I have currently no way to debug).
    This is unnerving
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Well, that's probably already the issue. Since you're on another thread you can not interact with gameobjects or most of Unity's API. You can only read or write gameobjects or components from the main thread.
     
  3. Ascanio1980

    Ascanio1980

    Joined:
    Jun 7, 2019
    Posts:
    51
    I am on it, am currently trying to find a simple enough tutorial about queuing.
    I did have a good simple example earlier today but can't find it... all articles I find now are hardcore TLDR.

    Anyway, yes, I have commented the UdpClient code and am iterating the Thread code to make it work from the editor first. Then will try it on the Windows machine. Then will finally test the .Receive() on the Windows machine.
     
  4. Ascanio1980

    Ascanio1980

    Joined:
    Jun 7, 2019
    Posts:
    51
    OK managed to work around the hideous Windows behavior with Threads.
    I start the thread and have it loop at 30Hz frequency (otherwise it eats up 50% of CPU budget on the SoC I am using), at each loop iteration it performs as many UdpClient.Receive() calls as needed until UdpClient.Available == 0, and Enqueues the messages in a Queue on the main thread. Then in the main thread I invoke the MessageReceived event Action as many times as necessary to empty the Queue, from the Update() loop.

    It feels dirty, and it's definitely not realtime anymore (but I can live with a worst case scenario delay of ~45ms, compounding a max time offset between when the message is sent, at 30Hz, and when it is read, plus 10-15ms between network latency and other lags that may creep in from OS side.

    I still don't know why UdpClient.BeginReceive() would crash on me on Windows, but I don't have the skill, time or energy to further investigate the matter now.