I'm using the Unity game engine to do some networking, and I want to use the built-in language features rather than rely on a third-party asset or Unity API. I've come up with this component, which attaches to a GameObject. When the game object is enabled, it should send UDP packets to a specified destination.
That part works. The trouble I'm having is with this sending
flag:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using UnityEngine;
namespace UdpTest
{
public class DummySender : MonoBehaviour
{
public string destinationAddress = "127.0.0.1";
public int port = 55555;
UdpClient client;
bool sending;
IPEndPoint endpoint;
private void Awake()
{
client = new UdpClient();
try
{
endpoint = new IPEndPoint(IPAddress.Parse(destinationAddress), port);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);
client.Connect(endpoint);
}
catch (Exception e)
{
Debug.LogError("Couldn't set up dummy sender: " + e.Message, gameObject);
gameObject.SetActive(false);
}
}
private void OnEnable()
{
sending = true;
new Task(() =>
{
try
{
Debug.Log("Ready to send");
while (sending)
{
var bytes = System.Text.Encoding.ASCII.GetBytes(DateTime.Now.Millisecond.ToString() + "\n");
client.Send(bytes, bytes.Length);
}
}
catch (Exception e)
{
Debug.LogError("Dummy send failed: " + e.Message);
}
}).Start();
}
private void OnDisable()
{
sending = false;
}
}
}
The issue I'm having is that I can't make it stop, once the task with the loop is running.
My plan was to make it stop when the component is disabled, by setting the sending
flag to false. I realize that setting the flag to false happens on a different thread. My expectation was that, yes, the loop may run a few more times between the sending
flag getting set to false and the task accessing its value, but that is fine for my purposes.
However, what is actually happening is that the task doesn't seem to ever become aware that the value of sending
has changed. It remains true
, The loop runs indefinitely, and my receiver doesn't stop receiving packets until I kill the program.
Why is this? I'm a bit new to low-level tasks/threading, but I understand that there's no guarantee that the worker task would be aware of the change immediately. I would have expected it to update eventually. But in my case, although I can verify that the sending
flag has been set to false on the main thread, the loop in the task never stops.
If I had to guess, this seems like the same kind of mistake as you get when you copy a value and change the original (i.e. a by reference/by value mistake). Does the task actually have a copy of sending
and not a reference to the class instance field? Or am I missing something else?
I know there are other strategies to achieve UDP communications, such as using the Async
variants but, for the sake of learning, I would like to know what I am doing wrong here and how to fix it without changing tack completely.