using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; public class BleUDP : MonoBehaviour { public static BleUDP ins; //单例记录 readonly string localIP = "127.0.0.1"; //本地IP readonly int myPort = 8001; //自身的注册端口 readonly int targetPort = 8000; //发消息的目标端口 Socket client; //UDP对象 float noneMsgTime = 0; //多久没收到字节消息了 Queue byteQueue = new Queue(); //接收信息的线程把字节信息放这里 Queue byteMainQueue = new Queue(); //主线程从byteQueue中取出数据后放在这里,供主线程update使用 public Action OnConnected; public Action OnConnectionFailed; public Action OnCharacteristicChanged; public Action OnScanEnded; [NonSerialized] public BluetoothStatusEnum bluetoothStatusEnum = BluetoothStatusEnum.Connect; void Awake() { if (ins) { Destroy(this.gameObject); return; } ins = this; DontDestroyOnLoad(this.gameObject); } bool _inited; void Init() { if (_inited) return; _inited = true; client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); client.Bind(new IPEndPoint(IPAddress.Parse(localIP), myPort)); /* C# UDP Socket ReceiveFrom 远程主机强迫关闭了一个现有的连接。 (经过反复研究,下述情况的原因终于搞清楚了,是"ICMP port unreachable"的问题,即:若向一个没有相应UDP监听端口的本机地址(比如127.0.0.1)发送UDP数据包,会回复ICMP port unreachable包,而这个包会被C#的UDP Socket ReceiveFrom函数得到,并报错为“远程主机强迫关闭了一个现有连接”!) (而为什么向存在的另一个ip(另一台主机)发送不会报错?因为防火墙!win10的防火墙把外部回复的ICMP port unreachable给屏蔽掉了。经实验,把防火墙关掉后,就会出现上述报错了。) (而为什么向一个不存在的ip地址发送不会报错?因为主机不存在,所以不会回复ICMP port unreachable) */ //配置socket参数,避免ICMP包导致的异常 uint IOC_IN = 0x80000000; uint IOC_VENDOR = 0x18000000; uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); Thread threadReciveMsg = new Thread(ReciveMsg); threadReciveMsg.Start(); } void OnDestroy() { if (ins == this) ins = null; if (client != null) client.Close(); } void Update() { lock (byteQueue) { while (byteQueue.Count > 0) { byteMainQueue.Enqueue(byteQueue.Dequeue()); } } if (bluetoothStatusEnum == BluetoothStatusEnum.Connecting) { if (noneMsgTime > 5) { bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail; EmitOnConnectionFailed(); return; } else if (byteMainQueue.Count > 0) { bluetoothStatusEnum = BluetoothStatusEnum.ConnectSuccess; EmitOnConnected(); } } else if (bluetoothStatusEnum == BluetoothStatusEnum.ConnectSuccess) { if (noneMsgTime > 5) { bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail; EmitOnConnectionFailed(); } } else { byteMainQueue.Clear(); return; } if (byteMainQueue.Count == 0) { noneMsgTime += Time.deltaTime; return; } noneMsgTime = 0; while (byteMainQueue.Count > 0) { EmitOnCharacteristicChanged(byteMainQueue.Dequeue()); } } public void SendMsg(string data) { Debug.LogWarning("sendMsgToBle: " + data); EndPoint point = new IPEndPoint(IPAddress.Parse(localIP), targetPort); client.SendTo(Encoding.UTF8.GetBytes(data), point); } void ReciveMsg() { while (true) { EndPoint point = new IPEndPoint(IPAddress.Any, 0); byte[] buffer = new byte[1024]; int length = client.ReceiveFrom(buffer, ref point); if (!point.ToString().EndsWith(targetPort.ToString())) continue; if (length <= 0) continue; byte[] bytes = new byte[length]; Array.Copy(buffer, bytes, length); lock (byteQueue) { byteQueue.Enqueue(bytes); } } } public void ScanNearbyDevices() { EmitOnScanEnded(); } public void Connect() { Init(); noneMsgTime = 0; bluetoothStatusEnum = BluetoothStatusEnum.Connecting; SendMsg("M"); } public void Disconnect() { bluetoothStatusEnum = BluetoothStatusEnum.Connect; } void EmitOnConnected() { try { OnConnected?.Invoke(); } catch (Exception) { } } void EmitOnConnectionFailed() { try { OnConnectionFailed?.Invoke(); } catch (Exception) { } } void EmitOnCharacteristicChanged(byte[] bytes) { try { OnCharacteristicChanged?.Invoke(bytes); } catch (Exception) { } } void EmitOnScanEnded() { try { OnScanEnded?.Invoke(); } catch (Exception) { } } }