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; } }