| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- using System;
- using System.Collections.Concurrent;
- using System.ComponentModel;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading.Tasks;
- using Newtonsoft.Json;
- using TeamAAS.Communication.Attributes;
- using TeamAAS.Communication.Base;
- using TeamAAS.Communication.Interfaces;
- using TeamAAS.Communication.Enums;
- namespace TeamAAS.Communication.Devices
- {
- [Communication("TCP 服务端", "基础通讯", "TCP 监听,接受客户端连接")]
- public class TcpServerCommunication : BindableCommunicationBase
- {
- [JsonIgnore]
- private TcpListener _listener;
- [JsonIgnore]
- private readonly object _sync = new object();
- [JsonIgnore]
- private readonly List<TcpClient> _clients = new List<TcpClient>();
- [JsonIgnore]
- private readonly ConcurrentQueue<string> _received = new ConcurrentQueue<string>();
- [JsonIgnore]
- private System.Threading.Timer _reconnectTimer;
- [JsonIgnore]
- private volatile bool _hasStartedOnce;
- [JsonIgnore]
- private volatile bool _manualDisconnect;
- [JsonIgnore]
- private volatile bool _isReconnecting;
- private string _localIp = "0.0.0.0";
- public string LocalIp
- {
- get { return _localIp; }
- set { SetProperty(ref _localIp, value); }
- }
- private int _port = 7790;
- public int Port
- {
- get { return _port; }
- set { SetProperty(ref _port, value); }
- }
- private Terminator _terminator = Terminator.None;
- [Category("III.数据格式"), DisplayName("结束符"), Description("发送时自动附加、接收时自动去除的结束符")]
- public Terminator Terminator
- {
- get { return _terminator; }
- set { SetProperty(ref _terminator, value); }
- }
- private DataEncoding _dataEncoding = DataEncoding.Default;
- [Category("III.数据格式"), DisplayName("编码格式"), Description("收发数据的编码格式")]
- public DataEncoding DataEncoding
- {
- get { return _dataEncoding; }
- set { SetProperty(ref _dataEncoding, value); }
- }
- [Browsable(false)]
- public override string EndpointUrl
- {
- get { return $"tcp://{LocalIp}:{Port}"; }
- set
- {
- if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("tcp://"))
- {
- var uri = new Uri(value);
- if (!string.IsNullOrWhiteSpace(uri.Host)) LocalIp = uri.Host;
- if (uri.Port > 0) Port = uri.Port;
- }
- }
- }
- public override bool IsConnected => _listener != null;
- public override event Action<object, bool> ConnectChangedEvent;
- public override event Action<object, string> DataReceivedEvent;
- public TcpServerCommunication()
- {
- }
- public override void Connect()
- {
- Disconnect();
- IPAddress bindIp = IPAddress.Any;
- if (!string.IsNullOrWhiteSpace(LocalIp))
- {
- try { bindIp = IPAddress.Parse(LocalIp.Trim()); }
- catch { bindIp = IPAddress.Any; }
- }
- _listener = new TcpListener(bindIp, Port);
- _listener.Start();
- _manualDisconnect = false;
- _hasStartedOnce = true;
- StopReconnectTimer();
- _ = Task.Run(AcceptLoop);
- ConnectChangedEvent?.Invoke(this, true);
- Notify(nameof(IsConnected));
- }
- public override Task ConnectAsync()
- {
- return Task.Run(() => Connect());
- }
- private async Task AcceptLoop()
- {
- try
- {
- while (_listener != null)
- {
- var client = await _listener.AcceptTcpClientAsync();
- lock (_sync) _clients.Add(client);
- _ = Task.Run(() => ClientLoop(client));
- }
- }
- catch
- {
- }
- finally
- {
- if (_hasStartedOnce && !_manualDisconnect)
- StartAutoReconnect();
- }
- }
- private async Task ClientLoop(TcpClient client)
- {
- var buffer = new byte[4096];
- var receiveBuffer = new StringBuilder();
- try
- {
- var stream = client.GetStream();
- while (client.Connected)
- {
- int n = await stream.ReadAsync(buffer, 0, buffer.Length);
- if (n <= 0) break;
- receiveBuffer.Append(GetEncoding().GetString(buffer, 0, n));
- FlushReceivedMessages(receiveBuffer);
- }
- }
- catch
- {
- }
- finally
- {
- lock (_sync) _clients.Remove(client);
- client.Dispose();
- }
- }
- /// <summary>
- /// 获取编码
- /// </summary>
- public Encoding GetEncoding()
- {
- switch (DataEncoding)
- {
- case DataEncoding.ASCII: return Encoding.ASCII;
- case DataEncoding.UTF7: return Encoding.UTF7;
- case DataEncoding.UTF8: return Encoding.UTF8;
- case DataEncoding.UTF32: return Encoding.UTF32;
- case DataEncoding.Unicode: return Encoding.Unicode;
- case DataEncoding.BigEndianUnicode: return Encoding.BigEndianUnicode;
- case DataEncoding.GB2312: return Encoding.GetEncoding("gb2312");
- default: return Encoding.Default;
- }
- }
- /// <summary>
- /// 获取结束符字符串
- /// </summary>
- public string GetTerminatorString()
- {
- switch (Terminator)
- {
- case Terminator.CR: return "\r";
- case Terminator.LF: return "\n";
- case Terminator.CRLF: return "\r\n";
- case Terminator.None:
- default: return string.Empty;
- }
- }
- /// <summary>
- /// 去除数据尾部结束符
- /// </summary>
- public string TrimTerminator(string text)
- {
- if (string.IsNullOrEmpty(text)) return text;
- var term = GetTerminatorString();
- if (string.IsNullOrEmpty(term)) return text;
- return text.EndsWith(term) ? text.Substring(0, text.Length - term.Length) : text;
- }
- /// <summary>
- /// 按结束符分帧:收到完整结束符才触发接收事件;无结束符时直接触发
- /// </summary>
- private void FlushReceivedMessages(StringBuilder receiveBuffer)
- {
- var term = GetTerminatorString();
- if (string.IsNullOrEmpty(term))
- {
- if (receiveBuffer.Length > 0)
- {
- var text = receiveBuffer.ToString();
- receiveBuffer.Clear();
- _received.Enqueue(text);
- DataReceivedEvent?.Invoke(this, text);
- }
- return;
- }
- string content = receiveBuffer.ToString();
- int idx;
- while ((idx = content.IndexOf(term, StringComparison.Ordinal)) >= 0)
- {
- var msg = content.Substring(0, idx);
- content = content.Substring(idx + term.Length);
- _received.Enqueue(msg);
- DataReceivedEvent?.Invoke(this, msg);
- }
- receiveBuffer.Clear();
- receiveBuffer.Append(content);
- }
- /// <summary>
- /// 自动重连:监听曾成功启动,意外停止后每5秒尝试重新监听
- /// </summary>
- private void StartAutoReconnect()
- {
- if (_reconnectTimer != null) return;
- if (_listener != null) { try { _listener.Stop(); } catch { } _listener = null; }
- lock (_sync)
- {
- foreach (var c in _clients) { try { c.Dispose(); } catch { } }
- _clients.Clear();
- }
- ConnectChangedEvent?.Invoke(this, false);
- Notify(nameof(IsConnected));
- _reconnectTimer = new System.Threading.Timer(ReconnectTimer_Elapsed, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
- }
- private void ReconnectTimer_Elapsed(object state)
- {
- if (_manualDisconnect) { StopReconnectTimer(); return; }
- if (_isReconnecting) return;
- _isReconnecting = true;
- try
- {
- if (_listener != null) { try { _listener.Stop(); } catch { } _listener = null; }
- IPAddress bindIp = IPAddress.Any;
- if (!string.IsNullOrWhiteSpace(LocalIp))
- {
- try { bindIp = IPAddress.Parse(LocalIp.Trim()); }
- catch { bindIp = IPAddress.Any; }
- }
- var listener = new TcpListener(bindIp, Port);
- try
- {
- listener.Start();
- _listener = listener;
- StopReconnectTimer();
- _ = Task.Run(AcceptLoop);
- ConnectChangedEvent?.Invoke(this, true);
- Notify(nameof(IsConnected));
- }
- catch
- {
- try { listener.Stop(); } catch { }
- }
- }
- finally
- {
- _isReconnecting = false;
- }
- }
- private void StopReconnectTimer()
- {
- var t = _reconnectTimer;
- _reconnectTimer = null;
- if (t != null) { try { t.Dispose(); } catch { } }
- }
- public void Send(string text)
- {
- var data = GetEncoding().GetBytes((text ?? string.Empty) + GetTerminatorString());
- lock (_sync)
- {
- foreach (var c in _clients.ToList())
- {
- try { c.GetStream().Write(data, 0, data.Length); }
- catch { }
- }
- }
- }
- public override object ReadValue(string address)
- {
- _received.TryDequeue(out var text);
- return text;
- }
- public override Task<object> ReadValueAsync(string address)
- {
- return Task.FromResult(ReadValue(address));
- }
- public override void WriteValue(string address, object value)
- {
- Send(value?.ToString() ?? string.Empty);
- }
- public override Task WriteValueAsync(string address, object value)
- {
- Send(value?.ToString() ?? string.Empty);
- return Task.CompletedTask;
- }
- public override void Disconnect()
- {
- StopReconnectTimer();
- _manualDisconnect = true;
- if (_listener != null)
- {
- _listener.Stop();
- _listener = null;
- }
- lock (_sync)
- {
- foreach (var c in _clients)
- {
- try { c.Dispose(); } catch { }
- }
- _clients.Clear();
- }
- ConnectChangedEvent?.Invoke(this, false);
- Notify(nameof(IsConnected));
- }
- public override void Dispose()
- {
- Disconnect();
- }
- }
- }
|