TcpServerCommunication.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.ComponentModel;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Newtonsoft.Json;
  11. using TeamAAS.Communication.Attributes;
  12. using TeamAAS.Communication.Base;
  13. using TeamAAS.Communication.Interfaces;
  14. using TeamAAS.Communication.Enums;
  15. namespace TeamAAS.Communication.Devices
  16. {
  17. [Communication("TCP 服务端", "基础通讯", "TCP 监听,接受客户端连接")]
  18. public class TcpServerCommunication : BindableCommunicationBase
  19. {
  20. [JsonIgnore]
  21. private TcpListener _listener;
  22. [JsonIgnore]
  23. private readonly object _sync = new object();
  24. [JsonIgnore]
  25. private readonly List<TcpClient> _clients = new List<TcpClient>();
  26. [JsonIgnore]
  27. private readonly ConcurrentQueue<string> _received = new ConcurrentQueue<string>();
  28. [JsonIgnore]
  29. private System.Threading.Timer _reconnectTimer;
  30. [JsonIgnore]
  31. private volatile bool _hasStartedOnce;
  32. [JsonIgnore]
  33. private volatile bool _manualDisconnect;
  34. [JsonIgnore]
  35. private volatile bool _isReconnecting;
  36. private string _localIp = "0.0.0.0";
  37. public string LocalIp
  38. {
  39. get { return _localIp; }
  40. set { SetProperty(ref _localIp, value); }
  41. }
  42. private int _port = 7790;
  43. public int Port
  44. {
  45. get { return _port; }
  46. set { SetProperty(ref _port, value); }
  47. }
  48. private Terminator _terminator = Terminator.None;
  49. [Category("III.数据格式"), DisplayName("结束符"), Description("发送时自动附加、接收时自动去除的结束符")]
  50. public Terminator Terminator
  51. {
  52. get { return _terminator; }
  53. set { SetProperty(ref _terminator, value); }
  54. }
  55. private DataEncoding _dataEncoding = DataEncoding.Default;
  56. [Category("III.数据格式"), DisplayName("编码格式"), Description("收发数据的编码格式")]
  57. public DataEncoding DataEncoding
  58. {
  59. get { return _dataEncoding; }
  60. set { SetProperty(ref _dataEncoding, value); }
  61. }
  62. [Browsable(false)]
  63. public override string EndpointUrl
  64. {
  65. get { return $"tcp://{LocalIp}:{Port}"; }
  66. set
  67. {
  68. if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("tcp://"))
  69. {
  70. var uri = new Uri(value);
  71. if (!string.IsNullOrWhiteSpace(uri.Host)) LocalIp = uri.Host;
  72. if (uri.Port > 0) Port = uri.Port;
  73. }
  74. }
  75. }
  76. public override bool IsConnected => _listener != null;
  77. public override event Action<object, bool> ConnectChangedEvent;
  78. public override event Action<object, string> DataReceivedEvent;
  79. public TcpServerCommunication()
  80. {
  81. }
  82. public override void Connect()
  83. {
  84. Disconnect();
  85. IPAddress bindIp = IPAddress.Any;
  86. if (!string.IsNullOrWhiteSpace(LocalIp))
  87. {
  88. try { bindIp = IPAddress.Parse(LocalIp.Trim()); }
  89. catch { bindIp = IPAddress.Any; }
  90. }
  91. _listener = new TcpListener(bindIp, Port);
  92. _listener.Start();
  93. _manualDisconnect = false;
  94. _hasStartedOnce = true;
  95. StopReconnectTimer();
  96. _ = Task.Run(AcceptLoop);
  97. ConnectChangedEvent?.Invoke(this, true);
  98. Notify(nameof(IsConnected));
  99. }
  100. public override Task ConnectAsync()
  101. {
  102. return Task.Run(() => Connect());
  103. }
  104. private async Task AcceptLoop()
  105. {
  106. try
  107. {
  108. while (_listener != null)
  109. {
  110. var client = await _listener.AcceptTcpClientAsync();
  111. lock (_sync) _clients.Add(client);
  112. _ = Task.Run(() => ClientLoop(client));
  113. }
  114. }
  115. catch
  116. {
  117. }
  118. finally
  119. {
  120. if (_hasStartedOnce && !_manualDisconnect)
  121. StartAutoReconnect();
  122. }
  123. }
  124. private async Task ClientLoop(TcpClient client)
  125. {
  126. var buffer = new byte[4096];
  127. var receiveBuffer = new StringBuilder();
  128. try
  129. {
  130. var stream = client.GetStream();
  131. while (client.Connected)
  132. {
  133. int n = await stream.ReadAsync(buffer, 0, buffer.Length);
  134. if (n <= 0) break;
  135. receiveBuffer.Append(GetEncoding().GetString(buffer, 0, n));
  136. FlushReceivedMessages(receiveBuffer);
  137. }
  138. }
  139. catch
  140. {
  141. }
  142. finally
  143. {
  144. lock (_sync) _clients.Remove(client);
  145. client.Dispose();
  146. }
  147. }
  148. /// <summary>
  149. /// 获取编码
  150. /// </summary>
  151. public Encoding GetEncoding()
  152. {
  153. switch (DataEncoding)
  154. {
  155. case DataEncoding.ASCII: return Encoding.ASCII;
  156. case DataEncoding.UTF7: return Encoding.UTF7;
  157. case DataEncoding.UTF8: return Encoding.UTF8;
  158. case DataEncoding.UTF32: return Encoding.UTF32;
  159. case DataEncoding.Unicode: return Encoding.Unicode;
  160. case DataEncoding.BigEndianUnicode: return Encoding.BigEndianUnicode;
  161. case DataEncoding.GB2312: return Encoding.GetEncoding("gb2312");
  162. default: return Encoding.Default;
  163. }
  164. }
  165. /// <summary>
  166. /// 获取结束符字符串
  167. /// </summary>
  168. public string GetTerminatorString()
  169. {
  170. switch (Terminator)
  171. {
  172. case Terminator.CR: return "\r";
  173. case Terminator.LF: return "\n";
  174. case Terminator.CRLF: return "\r\n";
  175. case Terminator.None:
  176. default: return string.Empty;
  177. }
  178. }
  179. /// <summary>
  180. /// 去除数据尾部结束符
  181. /// </summary>
  182. public string TrimTerminator(string text)
  183. {
  184. if (string.IsNullOrEmpty(text)) return text;
  185. var term = GetTerminatorString();
  186. if (string.IsNullOrEmpty(term)) return text;
  187. return text.EndsWith(term) ? text.Substring(0, text.Length - term.Length) : text;
  188. }
  189. /// <summary>
  190. /// 按结束符分帧:收到完整结束符才触发接收事件;无结束符时直接触发
  191. /// </summary>
  192. private void FlushReceivedMessages(StringBuilder receiveBuffer)
  193. {
  194. var term = GetTerminatorString();
  195. if (string.IsNullOrEmpty(term))
  196. {
  197. if (receiveBuffer.Length > 0)
  198. {
  199. var text = receiveBuffer.ToString();
  200. receiveBuffer.Clear();
  201. _received.Enqueue(text);
  202. DataReceivedEvent?.Invoke(this, text);
  203. }
  204. return;
  205. }
  206. string content = receiveBuffer.ToString();
  207. int idx;
  208. while ((idx = content.IndexOf(term, StringComparison.Ordinal)) >= 0)
  209. {
  210. var msg = content.Substring(0, idx);
  211. content = content.Substring(idx + term.Length);
  212. _received.Enqueue(msg);
  213. DataReceivedEvent?.Invoke(this, msg);
  214. }
  215. receiveBuffer.Clear();
  216. receiveBuffer.Append(content);
  217. }
  218. /// <summary>
  219. /// 自动重连:监听曾成功启动,意外停止后每5秒尝试重新监听
  220. /// </summary>
  221. private void StartAutoReconnect()
  222. {
  223. if (_reconnectTimer != null) return;
  224. if (_listener != null) { try { _listener.Stop(); } catch { } _listener = null; }
  225. lock (_sync)
  226. {
  227. foreach (var c in _clients) { try { c.Dispose(); } catch { } }
  228. _clients.Clear();
  229. }
  230. ConnectChangedEvent?.Invoke(this, false);
  231. Notify(nameof(IsConnected));
  232. _reconnectTimer = new System.Threading.Timer(ReconnectTimer_Elapsed, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
  233. }
  234. private void ReconnectTimer_Elapsed(object state)
  235. {
  236. if (_manualDisconnect) { StopReconnectTimer(); return; }
  237. if (_isReconnecting) return;
  238. _isReconnecting = true;
  239. try
  240. {
  241. if (_listener != null) { try { _listener.Stop(); } catch { } _listener = null; }
  242. IPAddress bindIp = IPAddress.Any;
  243. if (!string.IsNullOrWhiteSpace(LocalIp))
  244. {
  245. try { bindIp = IPAddress.Parse(LocalIp.Trim()); }
  246. catch { bindIp = IPAddress.Any; }
  247. }
  248. var listener = new TcpListener(bindIp, Port);
  249. try
  250. {
  251. listener.Start();
  252. _listener = listener;
  253. StopReconnectTimer();
  254. _ = Task.Run(AcceptLoop);
  255. ConnectChangedEvent?.Invoke(this, true);
  256. Notify(nameof(IsConnected));
  257. }
  258. catch
  259. {
  260. try { listener.Stop(); } catch { }
  261. }
  262. }
  263. finally
  264. {
  265. _isReconnecting = false;
  266. }
  267. }
  268. private void StopReconnectTimer()
  269. {
  270. var t = _reconnectTimer;
  271. _reconnectTimer = null;
  272. if (t != null) { try { t.Dispose(); } catch { } }
  273. }
  274. public void Send(string text)
  275. {
  276. var data = GetEncoding().GetBytes((text ?? string.Empty) + GetTerminatorString());
  277. lock (_sync)
  278. {
  279. foreach (var c in _clients.ToList())
  280. {
  281. try { c.GetStream().Write(data, 0, data.Length); }
  282. catch { }
  283. }
  284. }
  285. }
  286. public override object ReadValue(string address)
  287. {
  288. _received.TryDequeue(out var text);
  289. return text;
  290. }
  291. public override Task<object> ReadValueAsync(string address)
  292. {
  293. return Task.FromResult(ReadValue(address));
  294. }
  295. public override void WriteValue(string address, object value)
  296. {
  297. Send(value?.ToString() ?? string.Empty);
  298. }
  299. public override Task WriteValueAsync(string address, object value)
  300. {
  301. Send(value?.ToString() ?? string.Empty);
  302. return Task.CompletedTask;
  303. }
  304. public override void Disconnect()
  305. {
  306. StopReconnectTimer();
  307. _manualDisconnect = true;
  308. if (_listener != null)
  309. {
  310. _listener.Stop();
  311. _listener = null;
  312. }
  313. lock (_sync)
  314. {
  315. foreach (var c in _clients)
  316. {
  317. try { c.Dispose(); } catch { }
  318. }
  319. _clients.Clear();
  320. }
  321. ConnectChangedEvent?.Invoke(this, false);
  322. Notify(nameof(IsConnected));
  323. }
  324. public override void Dispose()
  325. {
  326. Disconnect();
  327. }
  328. }
  329. }