307 lines
11 KiB
C#
307 lines
11 KiB
C#
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)
|
||
{
|
||
serveData.RecvMsgTime = DateTime.Now;
|
||
_getData?.BeginInvoke(serveData, bytes, null, null);
|
||
_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);
|
||
}
|
||
}
|
||
|
||
/// <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))
|
||
{
|
||
foreach (var tcpServe in tcpServeConnectionDatas)
|
||
{
|
||
if (tcpServe.IsConnected != Enum.General.TrueFalseEnum.FALSE) continue;
|
||
tcpServe.IsConnected = default;
|
||
ConnectAsync(tcpServe);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <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 = [];
|
||
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]);
|
||
}
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
|
||
}
|