- Home /
UDP with coroutines crashing unity?
I'm working on a routine to receive UDP packets from an app. The system works fine when I use threading but using co-routines seems to just crash the editor outright on play! Any assistance would be welcomed. Someone suggested that it may be effecting the main thread of the program.
This is the script that crashed the editor.
using UnityEngine; using System.Collections; using System; using System.Text; using System.Net; using System.Net.Sockets; //using System.Threading; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary;
public class UdpScript : MonoBehaviour {
struct MyData
{
public char letters;
public byte byt;
public float dudX, dudY, dudZ;
public float NewX, NewY, NewZ;
public MyData Deserialize(byte[] data)
{
MyData result = new MyData();
using (MemoryStream inputStream = new MemoryStream(data))
using (BinaryReader reader = new BinaryReader(inputStream))
{
this.letters = reader.ReadChar();
this.letters = reader.ReadChar();
this.byt = reader.ReadByte();
this.byt = reader.ReadByte();
this.dudX = reader.ReadSingle();
this.dudY = reader.ReadSingle();
this.dudZ = reader.ReadSingle();
this.NewX = reader.ReadSingle();
this.NewY = reader.ReadSingle();
this.NewZ = reader.ReadSingle();
}
return result;
}
}
//private Thread receiveThread;
private UdpClient udp;
private bool showData, LoopHold, UdpBool;
public bool receivingbool;
public int port;
public float GyroX, GyroY, GyroZ, AccelX, AccelY, AccelZ, pitch, roll;
// Use this for initialization
void Start()
{
init();
initUDP();
}
// Update is called once per frame
void Update()
{
//StartCoroutine("TestCo");
if (Input.GetKeyDown(KeyCode.U))
UdpBool = !UdpBool;
while (LoopHold)
if (UdpBool)
StartCoroutine("RecieveData");
if (Input.GetKeyDown(KeyCode.D))
showData = !showData;
}
//initialise values
void init()
{
receivingbool = false;
showData = false;
LoopHold = true;
UdpBool = false;
}
//initialise the Udp thread to read data
void initUDP()
{
port = 9900;
udp = new UdpClient(port);
//receiveThread = new Thread(new ThreadStart(ReceiveData));
//receiveThread.IsBackground = true;
//receiveThread.Start();
}
//Display wether part is reciveing data from the digital twin and the accel values for caliration
void OnGUI()
{
if (showData)
{
Rect rectObj = new Rect(5, 5, 100, 100);
GUIStyle style = new GUIStyle();
style.alignment = TextAnchor.UpperRight;
GUI.Box(rectObj, "# UDPReceive: port :\n" + port + " #\n"
+ "\nRecieving Data \n" + receivingbool + " \n"
+ AccelX + "\n"
+ AccelY + "\n"
+ AccelZ, style); //receivingbool is set when a new udp client starts.
}
}
void ReadKeyInputs()
{
if (Input.GetKeyDown(KeyCode.D))
showData = !showData;
}
// receive thread
IEnumerator RecieveData()
{
try
{
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udp.Receive(ref anyIP);
MyData latestPacket = new MyData();
latestPacket.Deserialize(data);
GyroX = latestPacket.NewX;
GyroY = latestPacket.NewY;
GyroZ = latestPacket.NewZ;
AccelX = latestPacket.dudX;
AccelY = latestPacket.dudY;
AccelZ = latestPacket.dudZ;
}
catch (Exception err) //basic error handling, not particularly robust
{
print(err.ToString());
}
yield return null;
}
IEnumerator TestCo()
{
Debug.LogWarning("tips");
yield return null;
}
//close the UdpClient
public void EndClient()
{
if (receivingbool)
{
//if (receiveThread.IsAlive)
// receiveThread.Abort(); //Stop the curent thread
udp.Close(); //Close the whole client to avoid memory leaks
udp = null;
}//set the address off the previous client to null
LoopHold = false;
}
}
I'm using co-routines as WebGL and Unity aren't thread-safe.
while (LoopHold)
if (UdpBool)
StartCoroutine("RecieveData");
This loop creates new CoRoutines unti "LoopHold" becomes false. Is that what you intended?
In any case, you should never block the main thread by waiting for something. No matter how long it takes.
Answer by Roggert · Oct 07, 2016 at 09:48 AM
Thank you @doublemax that helped quite a bit. still crashing however. here is an update version of the code. if anyone has any advice its welcome.
public class UdpScript : MonoBehaviour {
struct MyData
{
public char letters;
public byte byt;
public float dudX, dudY, dudZ;
public float NewX, NewY, NewZ;
public MyData Deserialize(byte[] data)
{
MyData result = new MyData();
using (MemoryStream inputStream = new MemoryStream(data))
using (BinaryReader reader = new BinaryReader(inputStream))
{
this.letters = reader.ReadChar();
this.letters = reader.ReadChar();
this.byt = reader.ReadByte();
this.byt = reader.ReadByte();
this.dudX = reader.ReadSingle();
this.dudY = reader.ReadSingle();
this.dudZ = reader.ReadSingle();
this.NewX = reader.ReadSingle();
this.NewY = reader.ReadSingle();
this.NewZ = reader.ReadSingle();
}
return result;
}
}
//private Thread receiveThread;
private UdpClient udp;
private bool showData, UdpBool;
public bool receivingbool;
public int port;
public float GyroX, GyroY, GyroZ, AccelX, AccelY, AccelZ;
// Use this for initialization
void Start()
{
init();
initUDP();
}
// Update is called once per frame
void Update()
{
StartCoroutine("RecieveData");
if (Input.GetKeyDown(KeyCode.D))
showData = !showData;
if (Input.GetKeyDown(KeyCode.R))
receivingbool = !receivingbool;
}
//initialise values
void init()
{
receivingbool = false;
showData = false;
}
//initialise the Udp thread to read data
void initUDP()
{
port = 9900;
udp = new UdpClient(port);
}
//Display wether part is reciveing data from the digital twin and the accel values for caliration
void OnGUI()
{
if (showData)
{
Rect rectObj = new Rect(5, 5, 100, 100);
GUIStyle style = new GUIStyle();
style.alignment = TextAnchor.UpperRight;
GUI.Box(rectObj, "# UDPReceive: port :\n" + port + " #\n"
+ "\nRecieving Data \n" + receivingbool + " \n"
+ AccelX + "\n"
+ AccelY + "\n"
+ AccelZ, style); //receivingbool is set when a new udp client starts.
}
}
// receive thread
IEnumerator RecieveData()
{
if (receivingbool)
{
try
{
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udp.Receive(ref anyIP);
MyData latestPacket = new MyData();
latestPacket.Deserialize(data);
GyroX = latestPacket.NewX;
GyroY = latestPacket.NewY;
GyroZ = latestPacket.NewZ;
AccelX = latestPacket.dudX;
AccelY = latestPacket.dudY;
AccelZ = latestPacket.dudZ;
}
catch (Exception err) //basic error handling, not particularly robust
{
print(err.ToString());
}
}
yield return null;
}
//close the UdpClient
public void EndClient()
{
udp = null; //set the address off the previous client to null
udp.Close(); //Close the whole client to avoid memory leaks
}
}
You're still creating a new Coroutine in every Update() as soon as "receivingbool" becomes true. I'm not 100% which logic you want to achieve, but maybe something like this?
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.R))
{
receivingbool = !receivingbool;
if( receivingbool )
StartCoroutine("RecieveData");
else
StopCoroutine("RecieveData");
}
Also, i have never used UdpClient, but from what i've read, Receive() is a blocking call, so it's unsuitable in a single-threaded application.
I found BeginReceive() which works asynchronously with a callback: https://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive(v=vs.110).aspx
If that works, you may not even need a Coroutine for this.
// receive thread
IEnumerator RecieveData()...
Coroutines run on the main thread. They cannot be used as a substitute for threads. The only performance benefit they offer is, you can use yield return
to break up the actions inside the IEnumerator so that this one method executes over multiple frames.
Your IEnumerator method has one yield return at the end of the method where the method would return in any case. It's really just a normal method this way.
Whichever method you use (i.e. BeginReceive and a callback as @doublemax suggested), remember that Unity won't allow for example creating GameObjects from your own threads so read the received messages into a collection ( in the callback ) and use them from there in the Update of some $$anonymous$$onobehaviour if you do "anything Unity" with them