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.

429 lines
12 KiB

1 year ago
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<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
1 year ago
#endregion Fields
#region Ctors
private byte[] InOptionValues { get; set; }
1 year ago
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="port">监听的端口</param>
public AsyncTcpServer(int port) : this(IPAddress.Any, port)
1 year ago
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="ipep">监听的终结点</param>
public AsyncTcpServer(IPEndPoint ipep) : this(ipep.Address, ipep.Port)
1 year ago
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="address">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public AsyncTcpServer(IPAddress address, int port)
1 year ago
{
uint dummy = 0;
InOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(InOptionValues, 0);
BitConverter.GetBytes((uint)1000).CopyTo(InOptionValues, Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)1000).CopyTo(InOptionValues, Marshal.SizeOf(dummy) * 2);
Address = address;
Port = port;
1 year ago
Encoding = Encoding.Default;
Listener = new(address, port);
Clients = new();
Listener.AllowNatTraversal(true);
}
1 year ago
~AsyncTcpServer()
{
Stop();
1 year ago
}
#endregion Ctors
#region Server
/// <summary>
/// 启动服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public void Start()
1 year ago
{
Start(30);
1 year ago
}
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="backlog">服务器所允许的挂起连接序列的最大长度</param>
/// <returns>异步TCP服务器</returns>
public void Start(int backlog)
1 year ago
{
if (IsRunning()) return;
Listener.Start(backlog);
AcceptTcpClient(Listener);
1 year ago
}
/// <summary>
/// 停止服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public void Stop()
1 year ago
{
if (!IsRunning()) return;
1 year ago
try
{
Listener.Stop();
1 year ago
}
finally
1 year ago
{
foreach (var client in Clients.Values)
{
client.TcpClient.Client.Disconnect(false);
}
Clients.Clear();
1 year ago
}
}
1 year ago
public bool IsRunning()
{
return Listener != null && Listener.Server.IsBound;
1 year ago
}
public ICollection<TcpClientState> GetAllClient()
1 year ago
{
return Clients.Values;
1 year ago
}
#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(TcpClient client)
{
ClientConnected?.Invoke(this, new TcpClientConnectedEventArgs(client));
}
private void RaiseClientDisconnected(TcpClient client)
{
ClientDisconnected?.Invoke(this, new TcpClientDisconnectedEventArgs(client));
}
private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram)
{
DatagramReceived?.Invoke(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
}
#endregion Events
1 year ago
#region Receive
private void AcceptTcpClient(TcpListener listener)
1 year ago
{
listener.BeginAcceptTcpClient(HandleTcpClientAccepted, listener);
}
1 year ago
private void ReadBuffer(TcpClientState internalClient, NetworkStream networkStream)
{
networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, HandleDatagramReceived, internalClient);
}
private void HandleTcpClientAccepted(IAsyncResult ar)
{
if (!IsRunning()) return;
TcpListener? listener;
TcpClient? client;
1 year ago
try
{
listener = ar.AsyncState as TcpListener;
client = listener?.EndAcceptTcpClient(ar);
client?.Client.IOControl(IOControlCode.KeepAliveValues, InOptionValues, null);
1 year ago
}
catch (Exception)
1 year ago
{
return;
1 year ago
}
if (listener == null || client == null || !client.Connected) return;
1 year ago
byte[] buffer = new byte[client.ReceiveBufferSize];
TcpClientState internalClient = new(client, buffer);
1 year ago
// add client connection to cache
string clientKey = client.Client.RemoteEndPoint?.ToString() ?? "";
if (clientKey == "") return;
Clients.AddOrUpdate(clientKey, internalClient, (n, o) => { return internalClient; });
RaiseClientConnected(client);
1 year ago
// begin to read data
try
{
NetworkStream networkStream = internalClient.NetworkStream;
ReadBuffer(internalClient, networkStream);
1 year ago
}
catch (Exception)
1 year ago
{
Clients.TryRemove(clientKey, out _);
RaiseClientDisconnected(internalClient.TcpClient);
return;
1 year ago
}
// keep listening to accept next connection
AcceptTcpClient(listener);
1 year ago
}
private void HandleDatagramReceived(IAsyncResult ar)
1 year ago
{
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
Clients.TryRemove(clientKey, out _);
RaiseClientDisconnected(internalClient.TcpClient);
}
NetworkStream networkStream;
int readBytesNum;
1 year ago
try
{
networkStream = internalClient.NetworkStream;
// if the remote host has shutdown its connection,
// read will immediately return with zero bytes.
readBytesNum = networkStream.EndRead(ar);
1 year ago
}
catch (Exception)
1 year ago
{
return;
1 year ago
}
if (readBytesNum == 0)
{
// connection has been closed
Clients.TryRemove(clientKey, out _);
RaiseClientDisconnected(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);
1 year ago
}
#endregion Receive
#region Send
1 year ago
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient tcpClient, byte[] datagram)
{
if (!IsRunning()) return;
if (tcpClient == null || !tcpClient.Connected || datagram == null) return;
1 year ago
try
{
NetworkStream stream = tcpClient.GetStream();
if (stream.CanWrite)
{
stream.Write(datagram, 0, datagram.Length);
1 year ago
}
}
catch (Exception)
1 year ago
{
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient tcpClient, string datagram)
{
Send(tcpClient, Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(byte[] datagram)
{
if (!IsRunning()) return;
foreach (var client in Clients.Values)
1 year ago
{
Send(client.TcpClient, datagram);
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(string datagram)
{
if (!IsRunning()) return;
1 year ago
SendToAll(Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void SendAsync(TcpClient tcpClient, byte[] datagram)
1 year ago
{
if (!IsRunning()) return;
if (tcpClient == null || !tcpClient.Connected || datagram == null) return;
1 year ago
try
{
NetworkStream stream = tcpClient.GetStream();
if (stream.CanWrite)
{
stream.BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient);
1 year ago
}
}
catch (Exception)
1 year ago
{
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void SendAsync(TcpClient tcpClient, string datagram)
1 year ago
{
SendAsync(tcpClient, Encoding.GetBytes(datagram));
1 year ago
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAllAsync(byte[] datagram)
1 year ago
{
if (!IsRunning()) return;
foreach (var client in Clients.Values)
1 year ago
{
SendAsync(client.TcpClient, datagram);
1 year ago
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAllAsync(string datagram)
1 year ago
{
if (!IsRunning()) return;
SendToAllAsync(Encoding.GetBytes(datagram));
}
1 year ago
private void HandleDatagramWritten(IAsyncResult ar)
{
try
{
(ar.AsyncState as TcpClient)?.GetStream().EndWrite(ar);
}
catch (Exception)
{
}
1 year ago
}
#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)
1 year ago
{
if (disposing) Stop();
Disposed = true;
1 year ago
}
}
#endregion IDisposable Members
}