Program stopping at UDPClient.Receive() - timing issues?

Hello, I am trying to send packets of data into unity via UDP. The goal is then to use this data for inputs to move a cube in the game. The packets are json files that follow the structure {“name”: (double), “name2”: (double2), etc}.

I am having trouble getting passed the UDPClient.Receive() method inside my ThreadMethod. I am wondering if this is a timing/buffer issue between unity and the data coming in (data is being sent at 50Hz if that helps). Please see attached what is returned on the console.

Please also see my code below this. I am very new to unity and would appreciate any help to get this .Receive() working. Thanks in advance.

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;


public class Move : MonoBehaviour
{
    // Initialise vector for the gameObject (avatar) to move on.
    Vector3 Vec;
    // Intitialisations UDP and data processing
    //static UdpClient udp;
    Thread thread;
    //public string hostName = "127.0.0.1";
    static readonly object lockObject = new object();
    string returnData = "";
    bool translateData = false;

    // Start is called before the first frame update and is used to inititialise the UDP thread.
    void Start()
    {
        thread = new Thread(new ThreadStart(ThreadMethod));
        thread.Start();
    }

    // Update is called once per frame  
    public void Update()
    {
        if (translateData)
        {
            // Lock object so that multiple threads dont access data at the same time
            lock (lockObject)
            {
                translateData = false;
                UnityEngine.Debug.Log("Received: " + returnData);
                Functions();
                returnData = "";
            }
        }
    }

    // Handles the UDP communication from Host PC to Game PC
    private void ThreadMethod()
    {
        //Creates a UdpClient for reading incoming data.    
        UdpClient udp = new UdpClient(36864);
        UnityEngine.Debug.Log("Entering Thread method...");
        while (true)
        {
            UnityEngine.Debug.Log("Updating byte array...");
            Byte[] receiveBytes = new byte[0];
            try
            {
                // The IPEndPoint will allow to read datagrams sent from any source.
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
                UnityEngine.Debug.Log("Looking from any source...");


                // Set up receiver
                receiveBytes = udp.Receive(ref RemoteIpEndPoint);
                UnityEngine.Debug.Log("receiving bytes...");
            }
            catch (Exception err)
            {
                UnityEngine.Debug.Log("UDP Client Socket Exception Error: " + err);
            }
            // Lock object so that multiple threads dont access data at the same time
            lock (lockObject)
            {
                UnityEngine.Debug.Log("locked onto package...");
                returnData = Encoding.ASCII.GetString(receiveBytes);
                UnityEngine.Debug.Log("data is " + returnData.ToString());
                handInfo items = JsonUtility.FromJson<handInfo>(returnData);
                UnityEngine.Debug.Log("Array is " + items);
                if (items != null) { translateData = true; }
                else { UnityEngine.Debug.Log("Cannot reach data packages..."); }
            }
        }
    }
    // Class for displacement of each finger
    [Serializable]
    public class handInfo
    {
        public static double Pinky;
        public static double Ring;
        public static double Middle;
        public static double Index;
        public static double ThumbFlexExt;
        public static double ThumbAddAbd;
        //public static PlayerInfo CreateFromJSON(string jsonString)
        //{
        //    return JsonUtility.FromJson<PlayerInfo>(jsonString);
        //}
        // Given JSON input:
        // {"name":"Dr Charles","lives":3,"health":0.8}
        // this example will return a PlayerInfo object with
        // name == "Dr Charles", lives == 3, and health == 0.8f.
    }
    // Handles the movement associated with inputs
    void Functions()
    {
        if (handInfo.Pinky >= 7)
        {
            moveUp();
        }
        if (handInfo.Pinky <= 3)
        {
            moveDown();
        }
        if (handInfo.ThumbAddAbd <= 3)
        {
            moveLeft();
        }
        if (handInfo.ThumbAddAbd >= 7)
        {
            moveRight();
        }
    }
    public void moveRight()
    {
        Vec = transform.localPosition;
        Vec.x += Time.deltaTime * 20;
        transform.localPosition = Vec;
    }
    public void moveLeft()
    {
        Vec = transform.localPosition;
        Vec.x -= Time.deltaTime * 20;
        transform.localPosition = Vec;
    }
    public void moveUp()
    {
        Vec = transform.localPosition;
        Vec.y += Time.deltaTime * 20;
        transform.localPosition = Vec;
    }
    public void moveDown()
    {
        Vec = transform.localPosition;
        Vec.y -= Time.deltaTime * 20;
        transform.localPosition = Vec;
    }
}

Your code has several issues. First of all your sloppy thread handling will most likely cause issues in the long run. You should always make sure you gracefully terminate your thread once you’re done. As a fallback you usually want to call Abort on the thread when you exit your app / exit playmode. The same is true for closing the socket. Yes in when the UdpClient is garbage collected the socket would be closed automatically. However you never know when the GC runs or if it runs at all.

Your main issue is that your “handInfo” class does not contain any serializable fields. Static fields are never serialized by pretty much none serializers out there since static fields do not belong on an instance. So your FromJson does nothing.

Regardless of the fact that static fields are not serialized, the example json you mentioned in the comment wouldn’t have any relation to your class or the class fields. I specifically mean

   // {"name":"Dr Charles","lives":3,"health":0.8}

Wrong comments are much worst than no comments at all.

I tried your code in my test project and send test data to this port and it arrives just fine (apart from the fact that nothing is deserialized). I modifed your example and added a simple gui for sending test data locally:

using System.Threading;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System;


[Serializable]
public class HandInfo
{
    public double Pinky;
    public double Ring;
    public double Middle;
    public double Index;
    public double ThumbFlexExt;
    public double ThumbAddAbd;
    public override string ToString()
    {
        return "HandInfo: P:" + Pinky + " R:" + Ring + " M:" + Middle + " I:" + Index + " TFE:" + ThumbFlexExt + " TAA:" + ThumbAddAbd;
    }
}


public class UDPTest : MonoBehaviour
{
    UdpClient udp;
    Thread thread;
    static readonly object lockObject = new object();
    string returnData = "";
    HandInfo handInfo;

    private void Start()
    {
        thread = new Thread(new ThreadStart(UDPThreadMethod));
        thread.Start();
    }
    private void OnDestroy()
    {
        if (udp != null)
        {
            udp.Close();
            udp = null;
        }
        if (thread != null)
        {
            if (thread.Join(100))
                Debug.Log("UDP thread has terminated successfully");
            else
            {
                Debug.Log("UDP thread did not terminate within 100ms, forcefully aborting");
                thread.Abort();
            }
        }
    }

    private void Update()
    {
        if (handInfo != null)
        {
            lock (lockObject)
            {
                Debug.Log("Received: " + returnData);
                HandleHandInfo();
                handInfo = null;
            }
        }
    }

    private void UDPThreadMethod()
    {
        udp = new UdpClient(36864);
        Byte[] receiveBytes = new byte[0];
        string receivedText = "";
        while (udp != null)
        {
            try
            {
                receivedText = "";
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
                receiveBytes = udp.Receive(ref RemoteIpEndPoint);
                receivedText = Encoding.UTF8.GetString(receiveBytes);
                lock (lockObject)
                {
                    returnData = receivedText;
                    Debug.Log("received text: " + returnData.ToString());
                    handInfo = JsonUtility.FromJson<HandInfo>(returnData);
                    Debug.Log("parsed handinfo: " + handInfo);
                }
            }
            catch (Exception err)
            {
                if (udp != null)
                    Debug.Log("UDP Client Socket Exception Error: " + err);
                else
                    Debug.Log("thread is about to terminate ...");

            }
        }
    }

    private void HandleHandInfo()
    {
        if (handInfo == null)
            return;

        if (handInfo.Pinky >= 7)
            Move(Vector3.up);
        else if (handInfo.Pinky <= 3)
            Move(Vector3.down);

        if (handInfo.ThumbAddAbd <= 3)
            Move(Vector3.left);
        else if (handInfo.ThumbAddAbd >= 7)
            Move(Vector3.right);
    }

    private void Move(Vector3 aMovement)
    {
        transform.localPosition += aMovement * Time.deltaTime * 20;
    }


    // testing GUI

    private void TestSend(string aData)
    {
        // create temporary UdpClient for sending test data to our receiving socket
        UdpClient udpSender = new UdpClient();
        var data = Encoding.UTF8.GetBytes(aData);
        udpSender.Send(data, data.Length, "127.0.0.1", 36864);
        udpSender.Close();
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Dummy Test data"))
            TestSend("{\"Pinky\":5,\"Ring\":5,\"Middle\":5,\"Index\":5,\"ThumbFlexExt\":5,\"ThumbAddAbd\":5}");
        if (GUILayout.Button("up"))
            TestSend("{\"Pinky\":7,\"Ring\":5,\"Middle\":5,\"Index\":5,\"ThumbFlexExt\":5,\"ThumbAddAbd\":5}");
        if (GUILayout.Button("down"))
            TestSend("{\"Pinky\":3,\"Ring\":5,\"Middle\":5,\"Index\":5,\"ThumbFlexExt\":5,\"ThumbAddAbd\":5}");
        if (GUILayout.Button("left"))
            TestSend("{\"Pinky\":5,\"Ring\":5,\"Middle\":5,\"Index\":5,\"ThumbFlexExt\":5,\"ThumbAddAbd\":3}");
        if (GUILayout.Button("right"))
            TestSend("{\"Pinky\":5,\"Ring\":5,\"Middle\":5,\"Index\":5,\"ThumbFlexExt\":5,\"ThumbAddAbd\":7}");
    }
}

I heavily modified your code, refactored the method and class names (method and class names should start with a capital letter) and added a more appropriate thread termination code. The most important change is that I turned your static fields into actual instance member fields of your “HandInfo” class. Based on the field names I created several test data sets to cause the appropriate movement. For me this works fine. I can even run this in the editor and start a build of this application in parallel and use the buttons in the build to control the object inside the editor.

In your title you said something about your “Program is stopping”. I’m not sure what you meant by this. However if the data that arrives is not valid json an exception will be thrown. An unhandled exception inside a thread will terminate the thread. In my example everything that happens during the while loop is inside a try catch block, so the thread doesn’t terminate unless the “udp” variable is set to null.