2025-05-22 13:06:49 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
using WcsMain.DataBase.TableEntity;
|
|
|
|
|
|
using WcsMain.Tcp.Entity;
|
|
|
|
|
|
using WcsMain.WcsAttribute.AutoFacAttribute;
|
|
|
|
|
|
|
|
|
|
|
|
namespace WcsMain.Tcp.Client;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Wcs 的Tcp 客户端
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class BaseTcpClient
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 连接中事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData>? _connecting;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 连接失败事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData, Exception>? _connectFail;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 连接成功事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData>? _connectSuccess;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 连接断开事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData>? _connectOffline;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取数据事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData, string>? _getMessage;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取数据事件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<TcpServeConnectionData, byte[]>? _getData;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void SetConnecting(Action<TcpServeConnectionData> action) => _connecting = action;
|
|
|
|
|
|
public void SetConnectFailAction(Action<TcpServeConnectionData, Exception> action) => _connectFail = action;
|
|
|
|
|
|
public void SetConnectSuccess(Action<TcpServeConnectionData> action) => _connectSuccess = action;
|
|
|
|
|
|
public void SetConnectOffline(Action<TcpServeConnectionData> action) => _connectOffline = action;
|
|
|
|
|
|
public void GetMessage(Action<TcpServeConnectionData, string> action) => _getMessage = action;
|
|
|
|
|
|
public void SetGetData(Action<TcpServeConnectionData, byte[]> action) => _getData = action;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当前需要连接的服务端数量
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected List<TcpServeConnectionData> tcpServeConnectionDatas = [];
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 添加服务端基础数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="tcps"></param>
|
|
|
|
|
|
public void SetBaseTcpServe(List<AppTcp>? tcps)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tcps == default || tcps.Count == 0) return;
|
|
|
|
|
|
foreach (var tcp in tcps)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!CheckIpAndPort(tcp.TcpIp, tcp.TcpPort))
|
|
|
|
|
|
{
|
|
|
|
|
|
ConsoleLog.Warning($"【警告】TCP服务端地址:{tcp.TcpIp}:{tcp.TcpPort} 格式不满足要求");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
tcpServeConnectionDatas.Add(new()
|
|
|
|
|
|
{
|
|
|
|
|
|
DisplayName = tcp.DisplayName,
|
|
|
|
|
|
TcpServe = tcp
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 开始连接服务端
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Connect()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tcpServeConnectionDatas.Count < 1) return;
|
|
|
|
|
|
tcpServeConnectionDatas.ForEach(ConnectAsync);
|
|
|
|
|
|
ReConnectAsync(); // 开启重连属性
|
|
|
|
|
|
MonitorServeConnectedAsync(); // 检测客户端是否断开
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 连接到服务端并接收数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="serveData"></param>
|
|
|
|
|
|
private async void ConnectAsync(TcpServeConnectionData serveData)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if(serveData.IsConnected == Enum.General.TrueFalseEnum.TRUE) return;
|
|
|
|
|
|
_connecting?.Invoke(serveData);
|
|
|
|
|
|
serveData.TcpClient = new System.Net.Sockets.TcpClient();
|
|
|
|
|
|
serveData.TcpClient.Connect(serveData.TcpServe!.TcpIp!, (int)serveData.TcpServe.TcpPort!);
|
|
|
|
|
|
tcpServeConnectionDatas.Find(tcp => tcp.DisplayName == serveData.DisplayName)!.IsConnected = Enum.General.TrueFalseEnum.TRUE;
|
|
|
|
|
|
_connectSuccess?.Invoke(serveData);
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
var networkStream = serveData.TcpClient.GetStream();
|
|
|
|
|
|
byte[] bytes = new byte[1024];
|
|
|
|
|
|
int readLength = await networkStream.ReadAsync(bytes);
|
|
|
|
|
|
if (readLength > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
bytes = bytes.Take(readLength).ToArray();
|
|
|
|
|
|
serveData.RecvMsgTime = DateTime.Now;
|
|
|
|
|
|
_getData?.Invoke(serveData, bytes);
|
|
|
|
|
|
_getMessage?.Invoke(serveData, Encoding.ASCII.GetString(bytes));
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
_connectOffline?.Invoke(serveData);
|
|
|
|
|
|
tcpServeConnectionDatas.Find(tcp => tcp.DisplayName == serveData.DisplayName)!.IsConnected = Enum.General.TrueFalseEnum.FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_ = ex;
|
|
|
|
|
|
tcpServeConnectionDatas.Find(tcp => tcp.DisplayName == serveData.DisplayName)!.IsConnected = Enum.General.TrueFalseEnum.FALSE;
|
|
|
|
|
|
_connectFail?.Invoke(serveData, ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 重连
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void ReConnectAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tcpServeConnectionDatas.Count < 1) return;
|
|
|
|
|
|
CancellationTokenSource cts = new();
|
|
|
|
|
|
PeriodicTimer timer = new(new TimeSpan(0, 0, 0, 1, 0));
|
|
|
|
|
|
while (await timer.WaitForNextTickAsync(cts.Token))
|
|
|
|
|
|
{
|
2024-05-14 16:30:56 +08:00
|
|
|
|
foreach (var tcpServe in tcpServeConnectionDatas)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tcpServe.IsConnected != Enum.General.TrueFalseEnum.FALSE) continue;
|
2025-05-22 13:06:49 +08:00
|
|
|
|
tcpServe.IsConnected = default;
|
2024-05-14 16:30:56 +08:00
|
|
|
|
ConnectAsync(tcpServe);
|
2025-05-22 13:06:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 监控客户端是否断开
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public virtual async void MonitorServeConnectedAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tcpServeConnectionDatas.Count < 1) return;
|
|
|
|
|
|
CancellationTokenSource cts = new();
|
|
|
|
|
|
PeriodicTimer timer = new(new TimeSpan(0, 0, 0, 1, 0));
|
|
|
|
|
|
while (await timer.WaitForNextTickAsync(cts.Token))
|
|
|
|
|
|
{
|
|
|
|
|
|
List<Task> checkTasks = [];
|
2024-05-14 16:30:56 +08:00
|
|
|
|
foreach (var tcpServe in tcpServeConnectionDatas)
|
2025-05-22 13:06:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (tcpServe.IsConnected == Enum.General.TrueFalseEnum.FALSE || tcpServe.IsConnected == default) continue;
|
|
|
|
|
|
// 检测与服务端是否断开 ---- 多种检测方式并存
|
|
|
|
|
|
checkTasks.Add(Task.Factory.StartNew(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// Ping 地址
|
|
|
|
|
|
bool pingok = false;
|
|
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Net.NetworkInformation.Ping ping = new();
|
|
|
|
|
|
var pingResult = ping.Send(tcpServe.TcpServe!.TcpIp!, 100);
|
|
|
|
|
|
if (pingResult.Status != System.Net.NetworkInformation.IPStatus.Success) continue;
|
|
|
|
|
|
pingok = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if(!pingok)
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpServe.TcpClient?.Close();
|
|
|
|
|
|
//tcpServe.IsConnected = Enum.General.TrueFalseEnum.FALSE;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tcpServe.TcpClient == default) return;
|
|
|
|
|
|
// 尝试发送消息
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var networkStream = tcpServe.TcpClient.GetStream();
|
|
|
|
|
|
byte[] bytes = [];
|
|
|
|
|
|
networkStream.Write(bytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpServe.TcpClient?.Close();
|
|
|
|
|
|
//tcpServe.IsConnected = Enum.General.TrueFalseEnum.FALSE;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
Task.WaitAll([.. checkTasks]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 向指定别称的客户端发送消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
|
/// <param name="displayNames"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public TcpClientSendResult Send(byte[] value, params string[] displayNames)
|
|
|
|
|
|
{
|
|
|
|
|
|
TcpClientSendResult tcpClientSendResult = new() { Success = true };
|
|
|
|
|
|
// 指定发送
|
|
|
|
|
|
List<Task> sendTasks = [];
|
|
|
|
|
|
foreach (var tcpServe in tcpServeConnectionDatas)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (displayNames.Length > 0 && !displayNames.Contains(tcpServe.DisplayName)) { continue; }
|
|
|
|
|
|
if (tcpServe.TcpClient == default || tcpServe.IsConnected != Enum.General.TrueFalseEnum.TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpClientSendResult = new() { Success = false, Exception = new Exception($"别称:{tcpServe.DisplayName} 的Tcp连接不可用") };
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
sendTasks.Add(Task.Factory.StartNew(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var sendNetworkStream = tcpServe.TcpClient.GetStream();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
sendNetworkStream.Write(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpClientSendResult = new() { Success = false, Exception = ex };
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sendTasks.Count == 0) return new() { Success = false, Exception = new Exception("没有要发送的客户端") };
|
|
|
|
|
|
Task.WaitAll([.. sendTasks]);
|
|
|
|
|
|
return tcpClientSendResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 向指定别称的客户端发送消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
|
/// <param name="displayNames"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public TcpClientSendResult Send(string? value, params string[] displayNames)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(string.IsNullOrEmpty(value))
|
|
|
|
|
|
{
|
|
|
|
|
|
return new() { Success = false, Exception = new Exception("传入的值为空") };
|
|
|
|
|
|
}
|
|
|
|
|
|
TcpClientSendResult tcpClientSendResult = new() { Success = true };
|
|
|
|
|
|
// 指定发送
|
|
|
|
|
|
List<Task> sendTasks = [];
|
|
|
|
|
|
foreach (var tcpServe in tcpServeConnectionDatas)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (displayNames.Length > 0 && !displayNames.Contains(tcpServe.DisplayName)) { continue; }
|
|
|
|
|
|
if (tcpServe.TcpClient == default || tcpServe.IsConnected != Enum.General.TrueFalseEnum.TRUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpClientSendResult = new() { Success = false, Exception = new Exception($"别称:{tcpServe.DisplayName} 的Tcp连接不可用") };
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
sendTasks.Add(Task.Factory.StartNew(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var sendNetworkStream = tcpServe.TcpClient.GetStream();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
sendNetworkStream.Write(Encoding.ASCII.GetBytes(value));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
tcpClientSendResult = new() { Success = false, Exception = ex };
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(sendTasks.Count == 0) return new() { Success = false, Exception = new Exception("没有要发送的客户端") };
|
|
|
|
|
|
Task.WaitAll([.. sendTasks]);
|
|
|
|
|
|
return tcpClientSendResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查IP地址和端口号是否满足格式
|
|
|
|
|
|
/// 如果格式错误返回0,否则返回Int格式的端口号
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="ip"></param>
|
|
|
|
|
|
/// <param name="port"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private static bool CheckIpAndPort(string? ip, int? port)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* 检测 ip 格式是否正确 */
|
|
|
|
|
|
if (string.IsNullOrEmpty(ip)) return false;
|
|
|
|
|
|
string ipRegexString = "^((1[0-9][0-9]\\.)|(2[0-4][0-9]\\.)|(25[0-5]\\.)|([1-9][0-9]\\.)|([0-9]\\.)){3}((1[0-9][0-9])|(2[0-4][0-9])|(25[0-5])|([1-9][0-9])|([0-9]))$";
|
|
|
|
|
|
bool isIpOk = Regex.IsMatch(ip, ipRegexString);
|
|
|
|
|
|
if (!isIpOk) return false;
|
|
|
|
|
|
/* 检测 port 端口是否正确 */
|
|
|
|
|
|
return port >= 1 && port < 65535;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|