BleUDP.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Threading;
  9. public class BleUDP : MonoBehaviour
  10. {
  11. public static BleUDP ins; //单例记录
  12. readonly string localIP = "127.0.0.1"; //本地IP
  13. readonly int myPort = 8001; //自身的注册端口
  14. readonly int targetPort = 8000; //发消息的目标端口
  15. Socket client; //UDP对象
  16. float noneMsgTime = 0; //多久没收到字节消息了
  17. Queue<byte[]> byteQueue = new Queue<byte[]>(); //接收信息的线程把字节信息放这里
  18. Queue<byte[]> byteMainQueue = new Queue<byte[]>(); //主线程从byteQueue中取出数据后放在这里,供主线程update使用
  19. public Action OnConnected;
  20. public Action OnConnectionFailed;
  21. public Action<byte[]> OnCharacteristicChanged;
  22. public Action OnScanEnded;
  23. [NonSerialized] public BluetoothStatusEnum bluetoothStatusEnum = BluetoothStatusEnum.Connect;
  24. void Awake()
  25. {
  26. if (ins)
  27. {
  28. Destroy(this.gameObject);
  29. return;
  30. }
  31. ins = this;
  32. DontDestroyOnLoad(this.gameObject);
  33. }
  34. bool _inited;
  35. void Init()
  36. {
  37. if (_inited) return;
  38. _inited = true;
  39. client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  40. client.Bind(new IPEndPoint(IPAddress.Parse(localIP), myPort));
  41. /*
  42. C# UDP Socket ReceiveFrom 远程主机强迫关闭了一个现有的连接。
  43. (经过反复研究,下述情况的原因终于搞清楚了,是"ICMP port unreachable"的问题,即:若向一个没有相应UDP监听端口的本机地址(比如127.0.0.1)发送UDP数据包,会回复ICMP port unreachable包,而这个包会被C#的UDP Socket ReceiveFrom函数得到,并报错为“远程主机强迫关闭了一个现有连接”!)
  44. (而为什么向存在的另一个ip(另一台主机)发送不会报错?因为防火墙!win10的防火墙把外部回复的ICMP port unreachable给屏蔽掉了。经实验,把防火墙关掉后,就会出现上述报错了。)
  45. (而为什么向一个不存在的ip地址发送不会报错?因为主机不存在,所以不会回复ICMP port unreachable)
  46. */
  47. //配置socket参数,避免ICMP包导致的异常
  48. uint IOC_IN = 0x80000000;
  49. uint IOC_VENDOR = 0x18000000;
  50. uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
  51. client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
  52. Thread threadReciveMsg = new Thread(ReciveMsg);
  53. threadReciveMsg.Start();
  54. }
  55. void OnDestroy()
  56. {
  57. if (ins == this) ins = null;
  58. if (client != null) client.Close();
  59. }
  60. void Update()
  61. {
  62. lock (byteQueue)
  63. {
  64. while (byteQueue.Count > 0)
  65. {
  66. byteMainQueue.Enqueue(byteQueue.Dequeue());
  67. }
  68. }
  69. if (bluetoothStatusEnum == BluetoothStatusEnum.Connecting)
  70. {
  71. if (noneMsgTime > 5)
  72. {
  73. bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail;
  74. EmitOnConnectionFailed();
  75. return;
  76. }
  77. else if (byteMainQueue.Count > 0)
  78. {
  79. bluetoothStatusEnum = BluetoothStatusEnum.ConnectSuccess;
  80. EmitOnConnected();
  81. }
  82. }
  83. else if (bluetoothStatusEnum == BluetoothStatusEnum.ConnectSuccess)
  84. {
  85. if (noneMsgTime > 5)
  86. {
  87. bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail;
  88. EmitOnConnectionFailed();
  89. }
  90. }
  91. else
  92. {
  93. byteMainQueue.Clear();
  94. return;
  95. }
  96. if (byteMainQueue.Count == 0)
  97. {
  98. noneMsgTime += Time.deltaTime;
  99. return;
  100. }
  101. noneMsgTime = 0;
  102. while (byteMainQueue.Count > 0)
  103. {
  104. EmitOnCharacteristicChanged(byteMainQueue.Dequeue());
  105. }
  106. }
  107. public void SendMsg(string data)
  108. {
  109. Debug.LogWarning("sendMsgToBle: " + data);
  110. EndPoint point = new IPEndPoint(IPAddress.Parse(localIP), targetPort);
  111. client.SendTo(Encoding.UTF8.GetBytes(data), point);
  112. }
  113. void ReciveMsg()
  114. {
  115. while (true)
  116. {
  117. EndPoint point = new IPEndPoint(IPAddress.Any, 0);
  118. byte[] buffer = new byte[1024];
  119. int length = client.ReceiveFrom(buffer, ref point);
  120. if (!point.ToString().EndsWith(targetPort.ToString())) continue;
  121. if (length <= 0) continue;
  122. byte[] bytes = new byte[length];
  123. Array.Copy(buffer, bytes, length);
  124. lock (byteQueue)
  125. {
  126. byteQueue.Enqueue(bytes);
  127. }
  128. }
  129. }
  130. public void ScanNearbyDevices()
  131. {
  132. EmitOnScanEnded();
  133. }
  134. public void Connect()
  135. {
  136. Init();
  137. noneMsgTime = 0;
  138. bluetoothStatusEnum = BluetoothStatusEnum.Connecting;
  139. SendMsg("M");
  140. }
  141. public void Disconnect()
  142. {
  143. bluetoothStatusEnum = BluetoothStatusEnum.Connect;
  144. }
  145. void EmitOnConnected()
  146. {
  147. try
  148. {
  149. OnConnected?.Invoke();
  150. }
  151. catch (Exception) { }
  152. }
  153. void EmitOnConnectionFailed()
  154. {
  155. try
  156. {
  157. OnConnectionFailed?.Invoke();
  158. }
  159. catch (Exception) { }
  160. }
  161. void EmitOnCharacteristicChanged(byte[] bytes)
  162. {
  163. try
  164. {
  165. OnCharacteristicChanged?.Invoke(bytes);
  166. }
  167. catch (Exception) { }
  168. }
  169. void EmitOnScanEnded()
  170. {
  171. try
  172. {
  173. OnScanEnded?.Invoke();
  174. }
  175. catch (Exception) { }
  176. }
  177. }