using EC.Util.Common; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; namespace JiLinApp.Docking.VibrateAlarm; public class AsyncTcpServer : IDisposable { #region Fields private TcpListener Listener { get; set; } private ConcurrentDictionary Clients { get; } #region Properties /// /// 监听的IP地址 /// public IPAddress Address { get; private set; } /// /// 监听的端口 /// public int Port { get; private set; } /// /// 通信使用的编码 /// public Encoding Encoding { get; set; } public bool Disposed { get; private set; } #endregion Properties #endregion Fields #region Ctors /// /// 异步TCP服务器 /// /// 监听的端口 public AsyncTcpServer(int port) : this(IPAddress.Any, port) { } /// /// 异步TCP服务器 /// /// 监听的终结点 public AsyncTcpServer(IPEndPoint ipep) : this(ipep.Address, ipep.Port) { } /// /// 异步TCP服务器 /// /// 监听的IP地址 /// 监听的端口 public AsyncTcpServer(IPAddress address, int port) { Address = address; Port = port; Encoding = Encoding.Default; Listener = new(address, port); Clients = new(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Listener.AllowNatTraversal(true); } ~AsyncTcpServer() { Stop(); } #endregion Ctors #region Server /// /// 启动服务器 /// /// 异步TCP服务器 public void Start() { Start(30); } /// /// 启动服务器 /// /// 服务器所允许的挂起连接序列的最大长度 /// 异步TCP服务器 public void Start(int backlog) { if (IsRunning()) return; Listener.Start(backlog); AcceptTcpClient(Listener); } /// /// 停止服务器 /// /// 异步TCP服务器 public void Stop() { if (!IsRunning()) return; try { Listener.Stop(); } finally { foreach (var client in Clients.Values) { client.Client.Client.Disconnect(false); } Clients.Clear(); } } public bool IsRunning() { return Listener != null && Listener.Server.IsBound; } public ICollection GetAllClient() { return Clients.Values; } #endregion Server #region Events /// /// 与客户端的连接已建立事件 /// public event EventHandler? ClientConnected; /// /// 与客户端的连接已断开事件 /// public event EventHandler? ClientDisconnected; /// /// 接收到数据报文事件 /// public event EventHandler>? DatagramReceived; private void RaiseClientConnected(string clientAddr, TcpClientState clientState) { if (string.IsNullOrEmpty(clientAddr) || clientAddr.Equals(":")) return; Clients.AddOrUpdate(clientAddr, clientState, (n, o) => { return clientState; }); ClientConnected?.Invoke(this, new TcpClientConnectedEventArgs(clientState.Client)); } private void RaiseClientDisconnected(string clientAddr, TcpClient client) { if (string.IsNullOrEmpty(clientAddr) || clientAddr.Equals(":")) return; client.Client.Disconnect(false); if (Clients.TryRemove(clientAddr, out _)) { ClientDisconnected?.Invoke(this, new TcpClientDisconnectedEventArgs(client)); } } private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram) { DatagramReceived?.Invoke(this, new TcpDatagramReceivedEventArgs(sender, datagram)); } #endregion Events #region Receive private void AcceptTcpClient(TcpListener listener) { listener.BeginAcceptTcpClient(HandleTcpClientAccepted, listener); } private void HandleTcpClientAccepted(IAsyncResult ar) { if (!IsRunning()) return; if (ar.AsyncState is not TcpListener listener) return; TcpClient? client; try { client = listener.EndAcceptTcpClient(ar); if (client == null || !client.Connected) { client?.Client.Disconnect(false); return; } } catch (Exception) { return; } // add client connection to cache string clientAddr = client.ClientAddr(); byte[] buffer = new byte[client.ReceiveBufferSize]; TcpClientState clientState = new(client, buffer); RaiseClientConnected(clientAddr, clientState); // begin to read data ReadBuffer(clientState); // keep listening to accept next connection AcceptTcpClient(listener); } private void ReadBuffer(TcpClientState clientState) { try { NetworkStream stream = clientState.GetStream; if (clientState.IsRead) return; lock (clientState.IsReadLock) { if (clientState.IsRead) return; clientState.IsRead = true; stream.BeginRead(clientState.Buffer, 0, clientState.Buffer.Length, HandleDatagramReceived, clientState); } } catch (IOException e) { LogUnit.Error(e); return; } catch (Exception e) { LogUnit.Error(e); string clientAddr = clientState.Client.ClientAddr(); RaiseClientDisconnected(clientAddr, clientState.Client); } } private void HandleDatagramReceived(IAsyncResult ar) { if (!IsRunning()) return; if (ar.AsyncState is not TcpClientState clientState) return; int readNum; string clientAddr = clientState.Client.ClientAddr(); try { NetworkStream networkStream = clientState.GetStream; // if the remote host has shutdown its connection, read will immediately return with zero bytes. readNum = networkStream.EndRead(ar); if (readNum == 0) { RaiseClientDisconnected(clientAddr, clientState.Client); return; } } catch (Exception) { RaiseClientDisconnected(clientAddr, clientState.Client); return; } // received byte and trigger event notification byte[] receivedBytes = new byte[readNum]; Buffer.BlockCopy(clientState.Buffer, 0, receivedBytes, 0, readNum); RaiseDatagramReceived(clientState, receivedBytes); lock (clientState.IsReadLock) { clientState.IsRead = false; } // continue listening for tcp datagram packets ReadBuffer(clientState); } #endregion Receive #region Send /// /// 发送报文至指定的客户端 /// /// 客户端 /// 报文 public void Send(TcpClient client, byte[] datagram) { if (!IsRunning()) return; if (client == null || datagram == null) return; try { NetworkStream stream = client.GetStream(); stream.Write(datagram, 0, datagram.Length); } catch (Exception) { string clientAddr = client.ClientAddr(); RaiseClientDisconnected(clientAddr, client); } } /// /// 发送报文至指定的客户端 /// /// 客户端 /// 报文 public void Send(TcpClient client, string datagram) { Send(client, Encoding.GetBytes(datagram)); } /// /// 发送报文至所有客户端 /// /// 报文 public void SendToAll(byte[] datagram) { if (!IsRunning()) return; foreach (var client in Clients.Values) { Send(client.Client, datagram); } } /// /// 发送报文至所有客户端 /// /// 报文 public void SendToAll(string datagram) { if (!IsRunning()) return; SendToAll(Encoding.GetBytes(datagram)); } /// /// 发送报文至指定的客户端 /// /// 客户端 /// 报文 public bool SendAsync(TcpClient client, byte[] datagram) { if (!IsRunning()) return false; if (client == null || datagram == null) return false; try { NetworkStream stream = client.GetStream(); IAsyncResult result = stream.BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, client); return result.IsCompleted; } catch (Exception) { string clientAddr = client.ClientAddr(); RaiseClientDisconnected(clientAddr, client); } return false; } /// /// 发送报文至指定的客户端 /// /// 客户端 /// 报文 public bool SendAsync(TcpClient client, string datagram) { return SendAsync(client, Encoding.GetBytes(datagram)); } /// /// 发送报文至所有客户端 /// /// 报文 public void SendToAllAsync(byte[] datagram) { if (!IsRunning()) return; foreach (var client in Clients.Values) { SendAsync(client.Client, datagram); } } /// /// 发送报文至所有客户端 /// /// 报文 public void SendToAllAsync(string datagram) { if (!IsRunning()) return; SendToAllAsync(Encoding.GetBytes(datagram)); } private void HandleDatagramWritten(IAsyncResult ar) { if (ar.AsyncState is not TcpClient client) return; try { client.GetStream().EndWrite(ar); } catch (Exception) { string clientAddr = client.ClientAddr(); RaiseClientDisconnected(clientAddr, client); } } #endregion Send #region IDisposable Members /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; /// false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (!Disposed) { if (disposing) Stop(); Disposed = true; } } #endregion IDisposable Members }