无法在Unity游戏中接收UDP数据包

4

我有一个用Unity写的游戏,需要通过UDP实时接收数据。这些数据将通过无线方式从一款Java编写的安卓设备传输而来,而Unity程序则是用C#编写的。我的问题是,每当我尝试声明一个 UdpClient 对象,并在 Unity 的 Update() 方法中调用它的 Receive() 方法时,游戏就会挂起。以下是我试图放入 Update() 方法中的代码 -

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

但是这导致游戏停顿。

我尝试了不同的方法 - 我尝试在一个单独的线程中接收数据。如果我只需要接收字节数组,那么它就像魔法一样好用。没有问题。但是,我还需要使用接收到的数据作为实际游戏中使用的函数的参数(现在,我们假设我需要将接收到的数据字节作为字符串显示在主游戏窗口中)。但是,我不知道 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 { }
}

但是,如果我尝试上面的方法,我的程序就会卡住。那么,我到底缺少了什么?
1个回答

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,其中提供了一些示例。

谢谢。然而,当我这样做时,我会得到以下异常信息 -`SocketException: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。System.Net.Sockets.Socket.Bind (System.Net.EndPoint local_end) System.Net.Sockets.UdpClient.InitSocket (System.Net.EndPoint localEP) System.Net.Sockets.UdpClient..ctor (Int32 port) UDPTest.LoadClient () (at Assets/Scripts/UDPTest.cs:46) UDPTest.Start () (at Assets/Scripts/UDPTest.cs:35)` - Debadeep Sen
那个错误是由我在第二段解释的原因造成的。如果你将你的脚本添加到多个游戏对象中,你会得到多个副本同时执行(因此,几个套接字尝试打开相同的地址)。 - Isak Savo
实际上,即使我在独立的C#程序中使用它,错误仍然会发生,而不仅仅是在Unity中。这可能是因为服务器和监听器都在同一IP地址上使用相同的端口吗? - Debadeep Sen
取决于您如何定义“相同的端口”.. 如果它们都在侦听(即在同一个IPEndPoint上执行Receive()),那么是的-这是不允许的。每个主机:端口只允许一个侦听器。 - Isak Savo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接