BleUDP.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  34. client.Bind(new IPEndPoint(IPAddress.Parse(localIP), myPort));
  35. /*
  36. C# UDP Socket ReceiveFrom 远程主机强迫关闭了一个现有的连接。
  37. (经过反复研究,下述情况的原因终于搞清楚了,是"ICMP port unreachable"的问题,即:若向一个没有相应UDP监听端口的本机地址(比如127.0.0.1)发送UDP数据包,会回复ICMP port unreachable包,而这个包会被C#的UDP Socket ReceiveFrom函数得到,并报错为“远程主机强迫关闭了一个现有连接”!)
  38. (而为什么向存在的另一个ip(另一台主机)发送不会报错?因为防火墙!win10的防火墙把外部回复的ICMP port unreachable给屏蔽掉了。经实验,把防火墙关掉后,就会出现上述报错了。)
  39. (而为什么向一个不存在的ip地址发送不会报错?因为主机不存在,所以不会回复ICMP port unreachable)
  40. */
  41. //配置socket参数,避免ICMP包导致的异常
  42. uint IOC_IN = 0x80000000;
  43. uint IOC_VENDOR = 0x18000000;
  44. uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
  45. client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
  46. Thread threadReciveMsg = new Thread(ReciveMsg);
  47. threadReciveMsg.Start();
  48. }
  49. void OnDestroy()
  50. {
  51. if (ins == this) ins = null;
  52. client.Close();
  53. }
  54. void Update()
  55. {
  56. lock (byteQueue)
  57. {
  58. while (byteQueue.Count > 0)
  59. {
  60. byteMainQueue.Enqueue(byteQueue.Dequeue());
  61. }
  62. }
  63. if (bluetoothStatusEnum == BluetoothStatusEnum.Connecting)
  64. {
  65. if (noneMsgTime > 5)
  66. {
  67. bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail;
  68. EmitOnConnectionFailed();
  69. return;
  70. }
  71. else if (byteMainQueue.Count > 0)
  72. {
  73. bluetoothStatusEnum = BluetoothStatusEnum.ConnectSuccess;
  74. EmitOnConnected();
  75. }
  76. }
  77. else if (bluetoothStatusEnum == BluetoothStatusEnum.ConnectSuccess)
  78. {
  79. if (noneMsgTime > 5)
  80. {
  81. bluetoothStatusEnum = BluetoothStatusEnum.ConnectFail;
  82. EmitOnConnectionFailed();
  83. }
  84. }
  85. else
  86. {
  87. byteMainQueue.Clear();
  88. return;
  89. }
  90. if (byteMainQueue.Count == 0)
  91. {
  92. noneMsgTime += Time.deltaTime;
  93. return;
  94. }
  95. noneMsgTime = 0;
  96. while (byteMainQueue.Count > 0)
  97. {
  98. EmitOnCharacteristicChanged(byteMainQueue.Dequeue());
  99. }
  100. }
  101. public void SendMsg(string data)
  102. {
  103. Debug.LogWarning("sendMsgToBle: " + data);
  104. EndPoint point = new IPEndPoint(IPAddress.Parse(localIP), targetPort);
  105. client.SendTo(Encoding.UTF8.GetBytes(data), point);
  106. }
  107. void ReciveMsg()
  108. {
  109. while (true)
  110. {
  111. EndPoint point = new IPEndPoint(IPAddress.Any, 0);
  112. byte[] buffer = new byte[1024];
  113. int length = client.ReceiveFrom(buffer, ref point);
  114. if (!point.ToString().EndsWith(targetPort.ToString())) continue;
  115. if (length <= 0) continue;
  116. byte[] bytes = new byte[length];
  117. Array.Copy(buffer, bytes, length);
  118. lock (byteQueue)
  119. {
  120. byteQueue.Enqueue(bytes);
  121. }
  122. }
  123. }
  124. public void ScanNearbyDevices()
  125. {
  126. EmitOnScanEnded();
  127. }
  128. public void Connect()
  129. {
  130. noneMsgTime = 0;
  131. bluetoothStatusEnum = BluetoothStatusEnum.Connecting;
  132. SendMsg("M");
  133. }
  134. public void Disconnect()
  135. {
  136. bluetoothStatusEnum = BluetoothStatusEnum.Connect;
  137. }
  138. void EmitOnConnected()
  139. {
  140. try
  141. {
  142. OnConnected?.Invoke();
  143. }
  144. catch (Exception) { }
  145. }
  146. void EmitOnConnectionFailed()
  147. {
  148. try
  149. {
  150. OnConnectionFailed?.Invoke();
  151. }
  152. catch (Exception) { }
  153. }
  154. void EmitOnCharacteristicChanged(byte[] bytes)
  155. {
  156. try
  157. {
  158. OnCharacteristicChanged?.Invoke(bytes);
  159. }
  160. catch (Exception) { }
  161. }
  162. void EmitOnScanEnded()
  163. {
  164. try
  165. {
  166. OnScanEnded?.Invoke();
  167. }
  168. catch (Exception) { }
  169. }
  170. }