本文已参与「新人创作礼」活动,一起开启掘金创作之路
public class MyEventArgsBase : EventArgs
{
/// <summary>
/// 服务器IP地址列表
/// </summary>
public IPAddress[] Addresses { get; set; }
/// <summary>
/// 服务器端口
/// </summary>
public int Port { get; set; }
/// <summary>
/// ToString方法
/// </summary>
/// <returns></returns>
public override string ToString()
{
var res = new StringBuilder();
foreach (var item in Addresses)
{
res.Append($"{item},");
}
res.Remove(res.Length - 1, 1);
res.Append($"Addresses:{res} Port:{Port}");
return res.ToString();
}
}
/// <summary>
/// TCP客户端连接异常事件参数
/// </summary>
public class TcpCnnExceptionEventArgs : MyEventArgsBase
{
/// <summary>
/// 与服务器的连接发生异常事件参数
/// </summary>
/// <param name="ipAddresses">服务器IP地址列表</param>
/// <param name="port">服务器端口</param>
/// <param name="ex">内部异常</param>
public TcpCnnExceptionEventArgs(IPAddress[] ipAddresses, int port, Exception ex)
{
if (ipAddresses is null)
throw new ArgumentNullException("ipAddresses is null");
Addresses = ipAddresses;
Port = port;
Ex = ex;
}
/// <summary>
/// 内部异常
/// </summary>
public Exception Ex { get; private set; }
/// <summary>
/// ToString方法
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"{base.ToString()} Exception:{Ex}";
}
}
/// <summary>
/// TCP客户端与服务器的连接已建立事件参数
/// </summary>
public class TcpConnectedEventArgs : MyEventArgsBase
{
/// <summary>
/// 与服务器的连接已建立事件参数
/// </summary>
/// <param name="ipAddresses">服务器IP地址列表</param>
/// <param name="port">服务器端口</param>
public TcpConnectedEventArgs(IPAddress[] ipAddresses, int port)
{
if (ipAddresses is null)
throw new ArgumentNullException("ipAddresses is null");
Addresses = ipAddresses;
Port = port;
}
}
/// <summary>
/// TCP客户端与服务器的连接已断开事件参数
/// </summary>
public class TcpDisconnectedEventArgs : MyEventArgsBase
{
/// <summary>
/// 与服务器的连接已断开事件参数
/// </summary>
/// <param name="ipAddresses">服务器IP地址列表</param>
/// <param name="port">服务器端口</param>
public TcpDisconnectedEventArgs(IPAddress[] ipAddresses, int port)
{
if (ipAddresses is null)
throw new ArgumentNullException("ipAddresses is null");
Addresses = ipAddresses;
Port = port;
}
}
/// <summary>
/// TCP客户端接收到来自服务器端的报文事件参数
/// </summary>
public class TcpMsgReceivedEventArgs<T> : EventArgs
{
/// <summary>
/// 接收到报文事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="msg">报文</param>
/// <param name="encoding">报文编码方式</param>
public TcpMsgReceivedEventArgs(TcpClient tcpClient, T msg, Encoding encoding)
{
TcpClient = tcpClient;
Msg = msg;
Encoding = encoding;
}
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
/// <summary>
/// 报文
/// </summary>
public T Msg { get; private set; }
/// <summary>
/// 报文编码方式
/// </summary>
public Encoding Encoding { get; private set; }
}
/// <summary>
/// TCP客户端帮助类
/// </summary>
public class TcpClientUtil : IDisposable
{
#region 事件
/// <summary>
/// 接收到报文事件
/// </summary>
public event EventHandler<TcpMsgReceivedEventArgs<byte[]>> ReceiveMsg;
/// <summary>
/// 与服务器的连接已建立事件
/// </summary>
public event EventHandler<TcpConnectedEventArgs> ConnectServer;
/// <summary>
/// 与服务器的连接已断开事件
/// </summary>
public event EventHandler<TcpDisconnectedEventArgs> DisconnectServer;
/// <summary>
/// 与服务器的连接发生异常事件
/// </summary>
public event EventHandler<TcpCnnExceptionEventArgs> CnnExceptionOccurred;
#endregion 事件
#region 属性、字段
private readonly TcpClient _tcpClient;
private bool _disposed = false;
/// <summary>
/// 标识是否建立连接
/// </summary>
public bool Connected
{ get { return _tcpClient.Client.Connected; } }
/// <summary>
/// 远端服务器的IP地址列表
/// </summary>
public IPAddress[] Addresses { get; private set; }
/// <summary>
/// 远端服务器的端口
/// </summary>
public int Port { get; private set; }
private int _retries = 0;
/// <summary>
/// 连接重试次数
/// </summary>
public int Retries
{ get { return _retries; } set { _retries = value; } }
private int _retryInterval;
/// <summary>
/// 连接重试间隔
/// </summary>
public int RetryInterval
{
get { return _retryInterval; }
set { _retryInterval = value; }
}
/// <summary>
/// 远端服务器终结点
/// </summary>
public IPEndPoint ServerIPEndPoint
{
get { return new IPEndPoint(Addresses[0], Port); }
}
/// <summary>
/// 本地客户端终结点
/// </summary>
protected IPEndPoint LocalIPEndPoint { get; private set; }
private Encoding _encoding;
/// <summary>
/// 通信所使用的编码
/// </summary>
public Encoding Encoding
{ get { return _encoding; } set { _encoding = value; } }
#endregion 属性、字段
#region 方法
/// <summary>
/// 异步TCP客户端
/// </summary>
/// <param name="serverEP">远端服务器终结点</param>
/// <param name="localEP">本地客户端终结点</param>
/// <param name="retries">重试次数</param>
/// <param name="retryInterval">重试间隔,单位为秒</param>
/// <param name="encoding">报文编码方式</param>
public TcpClientUtil(IPEndPoint serverEP, IPEndPoint localEP = null, byte retries = 3, int retryInterval = 2, Encoding encoding = null)
: this(new[] { serverEP.Address }, serverEP.Port, localEP, retries, retryInterval, encoding)
{
}
/// <summary>
/// 异步TCP客户端
/// </summary>
/// <param name="serverIPAddress">远端服务器IP地址</param>
/// <param name="serverPort">远端服务器端口</param>
/// <param name="localEP">本地客户端终结点</param>
/// <param name="retries">重试次数</param>
/// <param name="retryInterval">重试间隔,单位为秒</param>
/// <param name="encoding">报文编码方式</param>
public TcpClientUtil(IPAddress serverIPAddress, int serverPort, IPEndPoint localEP = null, byte retries = 3, int retryInterval = 2, Encoding encoding = null)
: this(new[] { serverIPAddress }, serverPort, localEP, retries, retryInterval, encoding)
{
}
/// <summary>
/// 异步TCP客户端
/// </summary>
/// <param name="serverIPAddresses">远端服务器IP地址列表</param>
/// <param name="serverPort">远端服务器端口</param>
/// <param name="retries">重试次数</param>
/// <param name="retryInterval">重试间隔,单位为秒</param>
/// <param name="encoding">报文编码方式</param>
public TcpClientUtil(IPAddress[] serverIPAddresses, int serverPort, byte retries = 3, int retryInterval = 2, Encoding encoding = null)
: this(serverIPAddresses, serverPort, null, retries, retryInterval, encoding)
{
}
/// <summary>
/// 异步TCP客户端
/// </summary>
/// <param name="serverIPAddresses">远端服务器IP地址列表</param>
/// <param name="serverPort">远端服务器端口</param>
/// <param name="localEP">本地客户端终结点</param>
/// <param name="retries">重试次数</param>
/// <param name="retryInterval">重试间隔,单位为秒</param>
/// <param name="encoding">报文编码方式</param>
public TcpClientUtil(IPAddress[] serverIPAddresses, int serverPort, IPEndPoint localEP, byte retries = 3, int retryInterval = 2, Encoding encoding = null)
{
Addresses = serverIPAddresses;
Port = serverPort;
LocalIPEndPoint = localEP;
Encoding = encoding is null ? Encoding.UTF8 : encoding;
if (LocalIPEndPoint != null)
{
_tcpClient = new TcpClient(LocalIPEndPoint);
}
else
{
_tcpClient = new TcpClient();
}
Retries = retries;
RetryInterval = retryInterval;
}
/// <summary>
/// 连接到服务器
/// </summary>
/// <returns>异步TCP客户端</returns>
public TcpClientUtil Connect()
{
if (!Connected)
{
_tcpClient.BeginConnect(Addresses, Port, HandleTcpServerConnected, _tcpClient);
}
return this;
}
/// <summary>
/// 关闭与服务器的连接
/// </summary>
/// <returns>异步TCP客户端</returns>
public TcpClientUtil Close()
{
if (Connected)
{
_retries = 0;
_tcpClient.Close();
DisconnectServer?.BeginInvoke(this, new TcpDisconnectedEventArgs(Addresses, Port), null, null);
}
return this;
}
private void HandleTcpServerConnected(IAsyncResult ar)
{
try
{
_tcpClient.EndConnect(ar);
ConnectServer?.BeginInvoke(this, new TcpConnectedEventArgs(Addresses, Port), null, null);
_retries = 0;
}
catch (Exception ex)
{
//ExceptionHandler.Handle(ex);
//if (retries > 0)
//{
// Logger.Debug(string.Format(CultureInfo.InvariantCulture,
// "Connect to server with retry {0} failed.", retries));
//}
_retries++;
if (_retries > Retries)
{
// we have failed to connect to all the IP Addresses,
// connection has failed overall.
CnnExceptionOccurred?.BeginInvoke(this, new TcpCnnExceptionEventArgs(Addresses, Port, ex), null, null);
return;
}
else
{
//Logger.Debug(string.Format(CultureInfo.InvariantCulture,
// "Waiting {0} seconds before retrying to connect to server.",
// RetryInterval));
Thread.Sleep(TimeSpan.FromSeconds(RetryInterval));
Connect();
return;
}
}
// we are connected successfully and start asyn read operation.
byte[] buffer = new byte[_tcpClient.ReceiveBufferSize];
_tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, HandleMsgReceived, buffer);
}
private void HandleMsgReceived(IAsyncResult ar)
{
NetworkStream stream = _tcpClient.GetStream();
int numberOfReadBytes = 0;
try
{
numberOfReadBytes = stream.EndRead(ar);
}
catch
{
numberOfReadBytes = 0;
}
if (numberOfReadBytes == 0)
{
// connection has been closed
Close();
return;
}
// received byte and trigger event notification
byte[] buffer = (byte[])ar.AsyncState;
byte[] receivedBytes = new byte[numberOfReadBytes];
Buffer.BlockCopy(buffer, 0, receivedBytes, 0, numberOfReadBytes);
if (receivedBytes != null)
{
ReceiveMsg?.BeginInvoke(this, new TcpMsgReceivedEventArgs<byte[]>(_tcpClient, receivedBytes, _encoding), null, null);
}
stream.BeginRead(buffer, 0, buffer.Length, HandleMsgReceived, buffer); // then start reading from the network again
}
/// <summary>
/// 发送报文
/// </summary>
/// <param name="msg">报文</param>
public void Send(string msg)
{
Send(Encoding.GetBytes(msg));
}
/// <summary>
/// 发送报文
/// </summary>
/// <param name="msg">报文</param>
public void Send(byte[] msg)
{
_tcpClient.GetStream().BeginWrite(msg, 0, msg.Length, (IAsyncResult ar) => ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar), _tcpClient);
}
/// <summary>
/// Dispose方法
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); //标记gc不在调用析构函数
}
/// <summary>
/// 析构函数
/// </summary>
~TcpClientUtil()
{
Dispose(false);
}
/// <summary>
/// Dispose方法
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing)
{
if (_disposed) return; //如果已经被回收,就中断执行
if (disposing)
{
if (_tcpClient.Connected)
{
_tcpClient.Close();
}
_tcpClient.Dispose();
}
//TODO:释放非托管资源
_disposed = true;
}
#endregion 方法
}