using System.Text;
using System.Text.RegularExpressions;
using WcsMain.DataBase.TableEntity;
using WcsMain.Tcp.Entity;
using WcsMain.WcsAttribute.AutoFacAttribute;
namespace WcsMain.Tcp.Client;
///
/// Wcs 的Tcp 客户端
///
[Component]
public class BaseTcpClient
{
///
/// 连接中事件
///
private Action? _connecting;
///
/// 连接失败事件
///
private Action? _connectFail;
///
/// 连接成功事件
///
private Action? _connectSuccess;
///
/// 连接断开事件
///
private Action? _connectOffline;
///
/// 获取数据事件
///
private Action? _getMessage;
public void SetConnecting(Action action) => _connecting = action;
public void SetConnectFailAction(Action action) => _connectFail = action;
public void SetConnectSuccess(Action action) => _connectSuccess = action;
public void SetConnectOffline(Action action) => _connectOffline = action;
public void GetMessage(Action action) => _getMessage = action;
///
/// 当前需要连接的服务端数量
///
protected List tcpServeConnectionDatas = [];
///
/// 添加服务端基础数据
///
///
public void SetBaseTcpServe(List? 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
});
}
}
///
/// 开始连接服务端
///
public void Connect()
{
if (tcpServeConnectionDatas.Count < 1) return;
tcpServeConnectionDatas.ForEach(ConnectAsync);
ReConnectAsync(); // 开启重连属性
MonitorServeConnectedAsync(); // 检测客户端是否断开
}
///
/// 连接到服务端并接收数据
///
///
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)
{
serveData.RecvMsgTime = DateTime.Now;
_getMessage?.BeginInvoke(serveData, Encoding.ASCII.GetString(bytes), null, null);
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);
}
}
///
/// 重连
///
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))
{
foreach (var tcpServe in tcpServeConnectionDatas)
{
if (tcpServe.IsConnected != Enum.General.TrueFalseEnum.FALSE) continue;
tcpServe.IsConnected = default;
ConnectAsync(tcpServe);
}
}
}
///
/// 监控客户端是否断开
///
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 checkTasks = [];
foreach (var tcpServe in tcpServeConnectionDatas)
{
if (tcpServe.IsConnected == Enum.General.TrueFalseEnum.FALSE || tcpServe.IsConnected == default) continue;
// 检测与服务端是否断开 ---- 多种检测方式并存
checkTasks.Add(Task.Factory.StartNew(() =>
{
// Ping 地址
System.Net.NetworkInformation.Ping ping = new();
var pingResult = ping.Send(tcpServe.TcpServe!.TcpIp!, 100);
if(pingResult.Status != System.Net.NetworkInformation.IPStatus.Success)
{
tcpServe.TcpClient?.Close();
//tcpServe.IsConnected = Enum.General.TrueFalseEnum.FALSE;
return;
}
if (tcpServe.TcpClient == default) return;
// 尝试发送消息
var networkStream = tcpServe.TcpClient.GetStream();
byte[] bytes = [];
try
{
networkStream.Write(bytes);
}
catch
{
tcpServe.TcpClient?.Close();
//tcpServe.IsConnected = Enum.General.TrueFalseEnum.FALSE;
return;
}
}));
}
Task.WaitAll([.. checkTasks]);
}
}
///
/// 向指定别称的客户端发送消息
///
///
///
///
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 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;
}
///
/// 检查IP地址和端口号是否满足格式
/// 如果格式错误返回0,否则返回Int格式的端口号
///
///
///
///
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;
}
}