TCP客户端连接多个服务端的类
1.架构图
2.创建TCP 客户端与服务端通信的工具类
注:TcpClientAsyncTool类中是客户端连接服务端的,TcpClient 实质是Server,套用服务端连接客户端的,使用过程中自行修改名称,本案例暂未修改。连接使用异步操作,其余为同步执行的。
public class TcpClientAsyncTool
{
private TcpClient _tcpClient;
public bool isConnected = false;
public IPAddress IPAddress { get; private set; }
private int _port;// 端口号
private int _connectTimeout;// 连接超时时间(毫秒)
private byte[] _receiveBuffer = new byte[1024];// 接收服务端数据
/// <summary>
/// 服务端连接信
/// </summary>
/// <param name="ip">ip地址</param>
/// <param name="port">端口号</param>
/// <param name="connectTimeout">连接超时时间</param>
public TcpClientAsyncTool(string ip, int port, int connectTimeout = 2000)
{
_tcpClient = new TcpClient();
this.IPAddress = IPAddress.Parse(ip);
this._port = port;
this._connectTimeout = connectTimeout;
}
/// <summary>
/// 异步连接服务端
/// </summary>
/// <param name="connectDelegate"></param>
/// <returns></returns>
public async Task<(bool Success, string ErrorMsg)> ConnectAsync(Action connectDelegate = null)
{
if (_tcpClient.Connected)
{
isConnected = true;
return (true, string.Empty);
}
try
{
var connectTask = Task.Factory.FromAsync(
_tcpClient.BeginConnect(IPAddress, _port, null, null),
_tcpClient.EndConnect);
var timeoutTask = Task.Delay(_connectTimeout);
var completedTask = await Task.WhenAny(connectTask, timeoutTask);
if (completedTask == timeoutTask)
{
try { _tcpClient.Close(); } catch { }
return (false, "连接超时");
}
await connectTask;
if (!_tcpClient.Connected)
{
return (false, "连接失败");
}
connectDelegate?.Invoke();
isConnected = true;
return (true, string.Empty);
}
catch (Exception ex)
{
isConnected = false;
try { _tcpClient.Close(); } catch { }
return (false, ex.Message);
}
}
/// <summary>
/// 断开连接
/// </summary>
/// <param name="errorMsg">错误信息</param>
/// <returns>是否成功</returns>
public bool DisConnect(out string errorMsg)
{
errorMsg = string.Empty;
if (!_tcpClient.Connected)
{
isConnected = false;
return true;
}
try
{
_tcpClient.Close();
isConnected = false;
return true;
}
catch (Exception ex)
{
errorMsg = ex.Message;
isConnected = false;
return false;
}
}
/// <summary>
/// 同步发送数据
/// </summary>
/// <param name="sendBytes">要发送的字节数据</param>
/// <param name="errorMsg">错误信息</param>
/// <returns>是否发送成功</returns>
public bool SendData(byte[] sendBytes, out string errorMsg)
{
errorMsg = string.Empty;
if (!isConnected || !_tcpClient.Connected)
{
errorMsg = "客户端未连接";
return false;
}
if (sendBytes == null || sendBytes.Length == 0)
{
errorMsg = "发送数据不能为空";
return false;
}
try
{
NetworkStream networkStream = _tcpClient.GetStream();
if (!networkStream.CanWrite)
{
errorMsg = "网络流不可写";
return false;
}
IAsyncResult asyncResult = networkStream.BeginWrite(sendBytes, 0, sendBytes.Length, null, null);
networkStream.EndWrite(asyncResult);
return true;
}
catch (Exception ex)
{
errorMsg = ex.Message;
isConnected = false;
try { _tcpClient.Close(); } catch { }
return false;
}
}
/// <summary>
/// 接收数据
/// </summary>
/// <param name="result">是否接收成功</param>
/// <param name="errorMsg">错误信息</param>
/// <returns>接收到的字节数据,如果失败返回null</returns>
public byte[] ReceiveData(out bool result, out string errorMsg)
{
result = false;
errorMsg = string.Empty;
if (!isConnected || !_tcpClient.Connected)
{
errorMsg = "客户端未连接";
return null;
}
try
{
NetworkStream networkStream = _tcpClient.GetStream();
if (!networkStream.CanRead)
{
errorMsg = "网络流不可读";
return null;
}
// 清空接收缓冲区
Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);
IAsyncResult asyncResult = networkStream.BeginRead(_receiveBuffer, 0, _receiveBuffer.Length, null, null);
int readBytes = networkStream.EndRead(asyncResult);
if (readBytes <= 0)
{
isConnected = false;
errorMsg = "连接已关闭";
return null;
}
byte[] readByteArray = new byte[readBytes];
Array.Copy(_receiveBuffer, readByteArray, readBytes);
result = true;
return readByteArray;
}
catch (Exception ex)
{
errorMsg = ex.Message;
isConnected = false;
try { _tcpClient.Close(); } catch { }
return null;
}
}
}
3.创建类用于管理多个连接的服务端Server
public class TcpClientManager : IDisposable
{
private readonly List<(string Ip, int Port)> _clientConfigurations;// 存储服务端配置的IP和端口
private readonly ConcurrentDictionary<(string Ip, int Port), TcpClientAsyncTool> _clients =
new ConcurrentDictionary<(string Ip, int Port), TcpClientAsyncTool>();//为服务端创建一个字典,存储IP和端口对应的TcpClientAsyncTool对象
public event Action<string, byte[]> OnMessageReceivedWithIpPort;// 接收数据事件,包含IP和接收服务端返回的数据信息
public event Action<string, bool> OnConnectionStateChanged; //设备连接状态变化事件
private bool _disposed = false;// 确保Dispose方法只被调用一次
public TcpClientManager(List<(string Ip, int Port)> clientConfigurations)
{
_clientConfigurations = clientConfigurations ?? throw new ArgumentNullException(nameof(clientConfigurations));
}
/// <summary>
/// 初始化服务端连接
/// </summary>
/// <returns></returns>
public async Task InitializeAsync()
{
var tasks = new List<Task>();
foreach (var (ip, port) in _clientConfigurations)
{
tasks.Add(Task.Run(async () =>
{
var client = new TcpClientAsyncTool(ip, port);
if (_clients.TryAdd((ip, port), client))
{
await ConnectToServer(ip, port);
_ = StartReceivingMessages(ip, port);
}
}));
}
await Task.WhenAll(tasks);
}
public void SendCommandToServer(string ip, int port, byte[] byteData)
{
if (string.IsNullOrEmpty(ip))
throw new ArgumentException("IP address cannot be null or empty.", nameof(ip));
if (port <= 0 || port > 65535)
throw new ArgumentException("Port number is invalid.", nameof(port));
if (byteData == null)
throw new ArgumentNullException(nameof(byteData));
if (_clients.TryGetValue((ip, port), out var client))
{
string errorMsg = string.Empty;
client.SendData(byteData, out errorMsg);
if (!string.IsNullOrEmpty(errorMsg))
{
Console.WriteLine($"Error sending data to {ip}:{port}: {errorMsg}");
}
}
else
{
OnConnectionStateChanged?.Invoke(ip, false); // 触发断开事件
Console.WriteLine($"Client {ip}:{port} is not connected.");
}
}
/// <summary>
/// 判断客户端与服务端是否连接
/// </summary>
/// <param name="ip">IP</param>
/// <returns>返回连接状态</returns>
/// <exception cref="ArgumentException"></exception>
public bool IsDeviceConnected(string ip)
{
if (string.IsNullOrEmpty(ip))
throw new ArgumentException("IP address cannot be null or empty.", nameof(ip));
foreach (var client in _clients.Values)
{
if ((client.IPAddress).ToString() == (ip) && client.isConnected)
{
return true;
}
}
return false;
}
/// <summary>
/// 服务端接收数据处理
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
private async Task StartReceivingMessages(string ip, int port)
{
try
{
while (_clients.TryGetValue((ip, port), out var client) && client.isConnected)
{
try
{
bool result = false;
string errorMsg = string.Empty;
byte[] response = client.ReceiveData(out result, out errorMsg);
if (response != null && result)
{
OnMessageReceivedWithIpPort?.Invoke($"{ip}", response); //订阅接收数据事件,数据包含IP和端口
}
else if (!string.IsNullOrEmpty(errorMsg))
{
Console.WriteLine($"Receive error from {ip}:{port}: {errorMsg}");
break; // 退出循环,尝试重新连接
}
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data from {ip}:{port}: {ex.Message}");
break; // 退出循环,尝试重新连接
}
await Task.Delay(10);
}
}
finally
{
await Task.Delay(1000);
_ = ConnectToServer(ip, port);
_ = StartReceivingMessages(ip, port);
}
}
/// <summary>
/// 异步连接服务端
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
private async Task ConnectToServer(string ip, int port)
{
if (_disposed) return;
try
{
if (_clients.TryGetValue((ip, port), out var client))
{
if (!client.isConnected)
{
await client.ConnectAsync();
OnConnectionStateChanged?.Invoke(ip, client.isConnected); // 触发断开事件
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error connecting to {ip}:{port}: {ex.Message}");
}
}
/// <summary>
/// 断开连接到服务端
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void DisconnectToServer(string ip, int port)
{
if (_clients.TryRemove((ip, port), out var client))
{
string errorMsg = string.Empty;
client.DisConnect(out errorMsg);
OnConnectionStateChanged?.Invoke(ip, false); // 触发断开事件
if (!string.IsNullOrEmpty(errorMsg))
{
Console.WriteLine($"Error disconnecting {ip}:{port}: {errorMsg}");
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 关闭连接和释放资源
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 释放托管资源
foreach (var client in _clients.Values)
{
try
{
string errorMsg = string.Empty;
client.DisConnect(out errorMsg);
if (!string.IsNullOrEmpty(errorMsg))
{
Console.WriteLine($"Error during disconnect: {errorMsg}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during client disposal: {ex.Message}");
}
}
_clients.Clear();
}
_disposed = true;
}
}
~TcpClientManager()
{
Dispose(false);
}
}