Unity与Hololens 2通信故障:DatagramSocket_MessageReceived事件未触发求助
Unity HoloLens 2 UDP 接收数据不触发场景变化问题排查
问题描述
我做了一个Unity + HoloLens 2的网络项目,目标是给HoloLens发送数据,收到数据后触发场景变化。场景内有两个立方体,预期逻辑:
- 代码启动时,第一个立方体变为绿色;
- Socket连接建立后,该立方体变为红色;
- 收到数据时,两个立方体都变为黄色,但
ClientDatagramSocket_MessageReceived方法完全未生效。
在HoloLens上运行时,仅能看到立方体变红,无任何报错,接收数据的逻辑始终没触发。
原代码
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; #if !UNITY_EDITOR using Windows.Networking; using Windows.Networking.Sockets; #endif public class CubeControllerUWP : MonoBehaviour { public int listenPort = 5001; private Vector3 targetPosition; public GameObject cube; public GameObject cube2; private Material m; void Start() { m = new Material(cube.GetComponent<Renderer>().material); m.color = Color.green; #if !UNITY_EDITOR StartClient(); #endif } #if !UNITY_EDITOR private async void StartClient() { m.color = Color.green; cube.GetComponent<Renderer>().material = m; try { // Create the DatagramSocket and establish a connection to the echo server. var clientDatagramSocket = new Windows.Networking.Sockets.DatagramSocket(); clientDatagramSocket.MessageReceived += ClientDatagramSocket_MessageReceived; await clientDatagramSocket.BindServiceNameAsync("12345"); m.color = Color.red; cube.GetComponent<Renderer>().material = m; } catch (Exception ex) { Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult); } } #endif #if !UNITY_EDITOR private async void ClientDatagramSocket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args) { m.color = Color.yellow; cube2.GetComponent<Renderer>().material = m; cube.GetComponent<Renderer>().material = m; sender.Dispose(); /* string response; using (DataReader dataReader = args.GetDataReader()) { response = dataReader.ReadString(dataReader.UnconsumedBufferLength).Trim(); } */ // await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.clientListBox.Items.Add(string.Format("client received the response: \"{0}\"", response))); // await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.clientListBox.Items.Add("client closed its socket")); } #endif /* private Vector3 ParseDataToPosition(string data) { string[] parts = data.Split(','); float x = float.Parse(parts[0]); float y = float.Parse(parts[1]); float z = float.Parse(parts[2]); return new Vector3(x, y, z); } void Update() { transform.position = targetPosition; } void OnDestroy() { // udpClient.Close(); } */ }
问题定位
- 主线程限制:
MessageReceived回调在后台线程触发,Unity所有GameObject、Material操作必须在主线程执行,直接修改会失效,且无报错提示。 - Socket生命周期错误:回调里直接调用
sender.Dispose(),会立即销毁Socket,导致后续无法接收数据,甚至第一次接收也可能因Socket提前释放而失效。 - 端口不匹配:代码里
listenPort设为5001,但绑定的是硬编码的"12345",如果发送端往5001发数据,完全收不到。 - 异常处理无日志:catch块仅获取错误状态但未输出日志,无法排查Socket绑定阶段的隐性错误。
- Material复用问题:两个立方体共用同一个Material实例,虽不影响当前颜色修改,但后续可能引发材质状态混乱。
修复方案
核心修复点
- 新增主线程调度队列,把UI操作切回Unity主线程执行;
- 移除回调内的
sender.Dispose(),保证Socket持续监听; - 统一端口配置,用
listenPort变量绑定,避免硬编码; - 添加日志输出,方便排查各阶段问题;
- 为每个立方体创建独立Material实例,避免状态冲突。
修复后代码
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; #if !UNITY_EDITOR using Windows.Networking; using Windows.Networking.Sockets; #endif public class CubeControllerUWP : MonoBehaviour { public int listenPort = 5001; public GameObject cube; public GameObject cube2; private Material cubeMat; private Material cube2Mat; // 主线程操作队列 private readonly Queue<Action> _mainThreadActions = new Queue<Action>(); void Start() { // 为每个立方体创建独立Material实例 cubeMat = new Material(cube.GetComponent<Renderer>().material); cube2Mat = new Material(cube2.GetComponent<Renderer>().material); // 初始绿色 cubeMat.color = Color.green; cube.GetComponent<Renderer>().material = cubeMat; #if !UNITY_EDITOR StartClient(); #endif } void Update() { // 主线程处理队列中的操作 lock (_mainThreadActions) { while (_mainThreadActions.Count > 0) { _mainThreadActions.Dequeue().Invoke(); } } } #if !UNITY_EDITOR private async void StartClient() { try { var clientDatagramSocket = new DatagramSocket(); clientDatagramSocket.MessageReceived += ClientDatagramSocket_MessageReceived; // 使用配置的端口绑定 await clientDatagramSocket.BindServiceNameAsync(listenPort.ToString()); // 绑定成功后切主线程修改颜色并打日志 DispatchToMainThread(() => { cubeMat.color = Color.red; cube.GetComponent<Renderer>().material = cubeMat; Debug.Log($"Socket绑定成功,监听端口:{listenPort}"); }); } catch (Exception ex) { var errorStatus = SocketError.GetStatus(ex.GetBaseException().HResult); DispatchToMainThread(() => { Debug.LogError($"Socket初始化失败:{ex.Message},错误状态:{errorStatus}"); }); } } private void ClientDatagramSocket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { // 将颜色修改操作调度到主线程执行 DispatchToMainThread(() => { cubeMat.color = Color.yellow; cube2Mat.color = Color.yellow; cube.GetComponent<Renderer>().material = cubeMat; cube2.GetComponent<Renderer>().material = cube2Mat; Debug.Log("收到UDP数据,立方体已切换为黄色"); }); // 禁止在此销毁Socket,否则会停止监听 // sender.Dispose(); } // 主线程调度方法 private void DispatchToMainThread(Action action) { lock (_mainThreadActions) { _mainThreadActions.Enqueue(action); } } #endif }
额外检查项
- 确认发送端是往HoloLens的IP地址和
listenPort(5001)发送UDP数据; - 确保HoloLens防火墙允许该端口的UDP入站连接;
- 可在Unity编辑器用UDP工具(如NetAssist)模拟发送数据,先验证逻辑正确性;
- 通过HoloLens设备门户查看日志,排查隐性错误。
内容的提问来源于stack exchange,提问作者kirsal
相关产品推荐
相关产品推荐

