wcs_server_s7_baoying/WcsMain/Tcp/Client/BaseTcpClient.cs

315 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))
{
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 地址
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;
}
}