Cannot receive UDP packets inside Unity game

Debadeep Sen picture Debadeep Sen · May 29, 2012 · Viewed 7k times · Source

So, I have this game, written in Unity, which is supposed to receive data in real-time over UDP. The data will be coming over wireless from an android device, written in Java, while the Unity program is written in C#. My problem is, whenever I try to declare a UdpClient object, and call its Receive() method inside the Update() method of Unity, the game hangs. Here's the code that I am trying to put inside my Update() method -

UdpClient client = new UdpClient(9877);
IPEndPoint receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
byte[] recData = client.Receive(ref receivePoint);

But it's causing the game to hang.

I then tried a different approach - I tried to receive the data in a separate thread. Works like magic if all I have to do is receive the byte array. No issues. Except that I also need the data received to be used as parameters to functions used in the actual game (for now, let's just say I need to display the received data bytes as a string in the main game window). But, I do not have knowledge of how cross-threading works in Unity. I tried this -

string data = string.Empty;
private IPEndPoint receivePoint;

void OnGUI()
{
    GUI.Box(new Rect(20, 20, 100, 40), "");
    GUI.Label(new Rect(30, 30, 100, 40), data);        
}

void Start()
{
    LoadClient();
}

public void LoadClient()
{
    client = new UdpClient(9877);
    receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
    Thread startClient = new Thread(new ThreadStart(StartClient));
    startClient.Start();
}

public void StartClient()
{
    try
    {
        while (true)
        {
            byte[] recData = client.Receive(ref receivePoint);

            System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();
            data = encode.GetString(recData);
        }
    }
    catch { }
}

But my program hangs if I try the above. So, what exactly am I missing here?

Answer

Isak Savo picture Isak Savo · May 30, 2012

The reason it hangs for you is because that's the way Receive is defined. It blocks your current code until there is data available on the network connection (i.e. the underlying socket). You are correct that you should use a background thread for that.

Please note though, that creating threads in your game object scripts can be dangerous business in case you for example attach the script to multiple objects at the same time. You don't want multiple version of this script running at the same time because they would all try to bind to the same socket address (which won't work). You also need to pay attention to closing down the threads if the game object dies (this is not automatically done in C# - you have to stop threads).

That said, when you are using multiple threads you need to ensure thread safety. This means that you need to protect the data so that you cannot read it while it is being written to. The simplest way to do this is to use C# locks:

private readonly Object _dataLock = new Object();
private string _sharedData = String.Empty;

void OnGUI()
{
    string text = "";
    lock (_dataLock)
        text = _sharedData;
}

void StartClient()
{
    // ... [snip]
    var data = Encoding.ASCII.GetString(recData);
    lock (_dataLock)
        _sharedData = data;
}

Note that locks can hurt performance a bit, especially if used frequently. There are other ways to protect data in c# that are more performant (but slightly more complex). See this guideline from Microsoft for a few examples.