You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
12 KiB

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