所以,我有这个用Unity编写的游戏,它应该通过UDP实时接收数据。数据将通过用Java编写的Android设备进行无线传输,而Unity程序则用C#编写。我的问题是,每当我尝试声明一个UdpClient
对象,并在Unity的Receive()
方法中调用其Update()
方法时,游戏就会挂起。这是我试图在Update()
方法中添加的代码 -
UdpClient client = new UdpClient(9877);
IPEndPoint receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
byte[] recData = client.Receive(ref receivePoint);
但它导致游戏停滞不前。
然后我尝试了另一种方法 - 我试图在一个单独的线程中接收数据。像魔法一样工作,如果我只需要接收 byte
数组。没有问题。除了我还需要将接收到的数据用作实际游戏中使用的函数的参数(现在,我只想说我需要在主游戏窗口中将接收的数据字节显示为字符串)。但是,我不知道Unity中的跨线程如何工作。我试过了 -
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 { }
}
但是如果我尝试以上操作,我的程序就会挂起。那么,我到底错过了什么?
答案 0 :(得分:2)
它挂起的原因是因为Receive
的定义方式。它会阻止当前代码,直到网络连接上有可用数据(即底层套接字)。你是正确的,你应该使用后台线程。
请注意,在游戏对象脚本中创建线程可能是危险的事情,以防您例如同时将脚本附加到多个对象。您不希望同时运行此脚本的多个版本,因为它们都会尝试绑定到相同的套接字地址(这将不起作用)。 如果游戏对象死亡,你还需要注意关闭线程(这不是在C#中自动完成的 - 你必须停止线程)。
也就是说,当您使用多个线程时,您需要确保线程安全。这意味着您需要保护数据,以便在写入数据时无法读取数据。最简单的方法是使用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;
}
请注意,锁定会对性能造成一定影响,尤其是频繁使用时。还有其他方法可以保护c#中更高性能(但稍微复杂一点)的数据。有关示例,请参阅Microsoft的this guideline。