NodeMCU与Unity UDP通信卡顿修复及可调用SendData方法求助
问题根因分析
- 核心冻结原因:
Socket.ReceiveFrom是同步阻塞方法,在Unity主线程的Update生命周期中调用时,没有收到UDP包就会一直阻塞等待,直接卡住主线程导致Unity无响应 - 其他错误点:
- Unity代码的
Update末尾主动调用了server.Close(),首次帧运行后Socket就被销毁,后续发送功能完全失效 - 目标IP配置错误:NodeMCU固定IP为
192.168.43.20,Unity代码中写为192.162.43.209,网段不匹配无法通信 - NodeMCU回包逻辑未放在收到包的判断分支内,无连接时调用
Udp.remoteIP()会出现空地址异常
- Unity代码的
修复代码
第一步:修复NodeMCU端代码
把回包逻辑移到收到包的判断块内,避免空地址错误:
#include <ESP8266WiFi.h> #include <WiFiUdp.h> const char* ssid = "Epic SSID"; const char* password = "EpicPassword"; WiFiUDP Udp; unsigned int port = 25666; char packet[255]; IPAddress ip(192, 168, 43, 20); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0); void setup() { Serial.begin(115200); Serial.println(); WiFi.hostname("YVRB-01"); WiFi.config(ip, gateway, subnet); Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("Connection Successful"); Udp.begin(port); Serial.printf("Listener started at IP %s, at port %d", WiFi.localIP().toString().c_str(), port); } void loop() { int packetSize = Udp.parsePacket(); if (packetSize) { Serial.printf("Received %d bytes from %s, port %d", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort()); int len = Udp.read(packet, 255); if (len > 0) { packet[len] = 0; } Serial.printf("UDP packet contents: %s", packet); Serial.println(); // 回包逻辑移到此处,仅在收到包时回复 Udp.beginPacket (Udp.remoteIP(), Udp.remotePort()); Udp.write("Epic message"); Udp.endPacket(); } delay(300); }
第二步:修复Unity端代码
改用异步接收+非阻塞逻辑,保留SendData调用能力,不会卡住主线程:
using System; using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine; using System.Threading; public class UdpCommunicator : MonoBehaviour { [Header("UDP配置")] public string targetIp = "192.168.43.20"; public int targetPort = 25666; public int localListenPort = 25667; private Socket udpSocket; private IPEndPoint targetEndPoint; private Thread receiveThread; private byte[] receiveBuffer = new byte[1024]; private bool isRunning = false; void Start() { // 初始化Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); targetEndPoint = new IPEndPoint(IPAddress.Parse(targetIp), targetPort); // 绑定本地监听端口用于接收NodeMCU回包 udpSocket.Bind(new IPEndPoint(IPAddress.Any, localListenPort)); isRunning = true; // 接收逻辑放到独立子线程,不阻塞主线程 receiveThread = new Thread(ReceiveLoop); receiveThread.IsBackground = true; receiveThread.Start(); // 发送初始连接消息 SendData("I am connected"); } // 接收循环 运行在子线程 private void ReceiveLoop() { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); while (isRunning) { try { int recvLength = udpSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); string receivedMsg = Encoding.ASCII.GetString(receiveBuffer, 0, recvLength); Debug.Log($"收到UDP消息,来源:{remoteEndPoint},内容:{receivedMsg}"); } catch (SocketException e) { // 正常关闭时忽略异常 if (isRunning) Debug.LogError($"UDP接收错误:{e.Message}"); } Thread.Sleep(10); } } // 对外公开的发送方法,直接调用即可 public void SendData(string message) { if (udpSocket == null || !isRunning) return; byte[] sendData = Encoding.ASCII.GetBytes(message); udpSocket.SendTo(sendData, sendData.Length, SocketFlags.None, targetEndPoint); Debug.Log($"已发送UDP消息:{message}"); } // 释放资源 private void OnDestroy() { isRunning = false; receiveThread?.Join(100); udpSocket?.Close(); udpSocket?.Dispose(); } }
使用说明
- 确认NodeMCU和运行Unity的设备连接在同一个WiFi下
- 把Unity代码挂到场景任意活跃GameObject上,检查Inspector面板的目标IP和端口是否和NodeMCU的配置一致
- 任意逻辑中获取
UdpCommunicator组件后,调用SendData("自定义内容")即可发送消息 - 运行后Unity控制台会打印收发消息日志,NodeMCU的串口也会输出收到的内容
内容的提问来源于stack exchange,提问作者YGreater
相关产品推荐
相关产品推荐

