Unity中C# UDP发送正常但无法接收的问题求助
解决Unity UDP无法接收PLC数据包的问题
看起来你已经把网络层面的排查做的很到位了——端口被Unity占用、Wireshark能抓到包、本地回环能接收,这些都说明核心的网络链路是通的,问题大概率出在套接字的配置或者Unity线程/环境的细节上。我给你梳理几个针对性的排查和修复方向:
1. 明确绑定本地以太网IP,避免多网卡路由歧义
你当前用new UdpClient(8052,AddressFamily.InterNetwork)默认绑定的是0.0.0.0(所有可用网络接口),但在直连以太网的场景下,明确绑定到你的笔记本以太网IP(192.168.0.41)能避免系统在多网卡(比如还有Wi-Fi)下的路由优先级问题。修改绑定逻辑:
// 替换ReceiveData里的UdpClient初始化代码 IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.41"), 8052); using (var udpClient = new UdpClient(localEndPoint)) { // 后续接收逻辑... }
2. 修复线程与Unity主线程的交互问题
你在接收线程里频繁调用print,但Unity的print是主线程专属API,跨线程频繁调用可能会引发隐式的线程阻塞或者数据丢失。建议改用线程安全的队列存储接收数据,在Update里统一处理日志和数据:
// 新增线程安全队列存储接收数据 private ConcurrentQueue<byte[]> _receivedDataQueue = new ConcurrentQueue<byte[]>(); private void ReceiveData() { IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.41"), 8052); using (var udpClient = new UdpClient(localEndPoint)) { udpClient.EnableBroadcast = true; // 确保支持广播(如果PLC用广播发送) while (canReceive) { try { IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); var receivedResults = udpClient.Receive(ref remoteEndPoint); _receivedDataQueue.Enqueue(receivedResults); // 这里用Debug.LogWarning或者直接记录到线程安全的日志变量,不要用print } catch (SocketException ex) { if (ex.SocketErrorCode != SocketError.Interrupted) { Debug.LogError($"UDP接收错误: {ex.Message}"); } } } } } // 在Update里处理队列数据 void Update() { while (_receivedDataQueue.TryDequeue(out byte[] data)) { Debug.Log($"成功接收{data.Length}字节数据"); // 解析两个float的逻辑 if (data.Length == 8) { float val1 = BitConverter.ToSingle(data, 0); float val2 = BitConverter.ToSingle(data, 4); Debug.Log($"解析结果: {val1}, {val2}"); } } // 你的其他Update逻辑... }
3. 添加套接字关键选项配置
尝试设置几个UDP套接字的优化选项,避免因缓冲区过小或者协议特性导致丢包:
// 在UdpClient初始化后添加 udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); udpClient.Client.ReceiveBufferSize = 1024 * 1024; // 增大接收缓冲区到1MB
4. 确保线程能被安全终止
在脚本销毁时要正确终止接收线程,避免套接字资源泄漏:
void OnDestroy() { canReceive = false; receiveThread?.Join(1000); // 等待线程优雅终止 receiveThread = null; }
5. 用最小化脚本排除第三方干扰
你当前项目里导入了PlayMaker,有可能是第三方插件的网络钩子或者线程管理影响了UDP接收。可以新建一个空Unity项目,只导入下面这个最小化测试脚本,再测试PLC的接收:
using System; using System.Net; using System.Net.Sockets; using UnityEngine; using System.Threading; using System.Collections.Concurrent; public class MinimalUDPTest : MonoBehaviour { private Thread _receiveThread; private UdpClient _udpClient; private bool _isRunning; private ConcurrentQueue<byte[]> _dataQueue = new ConcurrentQueue<byte[]>(); public int ListenPort = 8052; public string LocalIP = "192.168.0.41"; void Start() { _isRunning = true; _receiveThread = new Thread(ReceiveLoop) { IsBackground = true }; _receiveThread.Start(); } void ReceiveLoop() { try { IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(LocalIP), ListenPort); _udpClient = new UdpClient(localEP); _udpClient.EnableBroadcast = true; while (_isRunning) { IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); byte[] data = _udpClient.Receive(ref remoteEP); _dataQueue.Enqueue(data); Debug.Log($"从{remoteEP.Address}收到{data.Length}字节"); } } catch (SocketException ex) { if (_isRunning) Debug.LogError($"UDP错误: {ex.Message}"); } finally { _udpClient?.Close(); } } void Update() { while (_dataQueue.TryDequeue(out byte[] data)) { if (data.Length == 8) { float v1 = BitConverter.ToSingle(data, 0); float v2 = BitConverter.ToSingle(data, 4); Debug.Log($"解析值: {v1:F2}, {v2:F2}"); } } } void OnDestroy() { _isRunning = false; _udpClient?.Close(); _receiveThread?.Join(1000); } }
最后几个排查点
- 确认PLC发送的目标IP是
192.168.0.41,端口是8052,没有误设为广播地址(如果是广播,上面的脚本已经开启了广播接收) - 检查Windows防火墙的出站规则,虽然你开放了入站,但有时候系统会限制UDP的出站响应(不过你的Wireshark能看到Unity发的包,这条可能性较低)
- 确认笔记本的以太网适配器没有开启任何特殊的网络优化(比如QoS、节能模式)
内容的提问来源于stack exchange,提问作者Dehnungsmessstreifen
相关产品推荐
相关产品推荐

