362 lines
12 KiB
C#
362 lines
12 KiB
C#
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using SocketTool.Entity;
|
||
|
||
namespace SocketTool;
|
||
|
||
/// <summary>
|
||
/// socket 客户端 .net6 版
|
||
/// </summary>
|
||
public class SocketClient
|
||
{
|
||
#region 全局变量
|
||
|
||
private List<SocketModel> NeedConnectSockets = []; // 需要连接的socket
|
||
|
||
int IPCount;//传入的连接数
|
||
|
||
#endregion
|
||
|
||
#region 全局属性
|
||
/// <summary>
|
||
/// 字符串解析格式
|
||
/// </summary>
|
||
private Encoding? Encod { get; set; }
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// socket连接类,自带自动重连功能
|
||
/// 传入参数示例:127.0.0.1:8090
|
||
/// </summary>
|
||
/// <param name="encoding"></param>
|
||
/// <param name="IPPorts"></param>
|
||
/// <exception cref="Exception"></exception>
|
||
public SocketClient(Encoding? encoding, params string?[] IPPorts)
|
||
{
|
||
Encod = encoding;
|
||
IPCount = IPPorts.Length;//数量赋值
|
||
foreach (string? IPPort in IPPorts)
|
||
{
|
||
if (string.IsNullOrEmpty(IPPort))
|
||
{
|
||
continue;
|
||
}
|
||
string ipAndPort = IPPort.Replace(":", ":");//将中文冒号替换成英文冒号
|
||
if (!ipAndPort.Contains(':'))
|
||
{
|
||
// 不含有端口号的直接抛异常,必须处理
|
||
throw new Exception($"地址:{IPPort} 格式不正确,必须处理以后才能正常运行。");
|
||
}
|
||
string[] ipp = ipAndPort.Split(':');
|
||
if (ipp.Length != 2 || string.IsNullOrEmpty(ipp[0]) || string.IsNullOrEmpty(ipp[1]))
|
||
{
|
||
// 拆分后没有地址或者端口号的直接抛异常,必须处理
|
||
throw new Exception($"地址:{IPPort} 格式不正确,必须处理以后才能正常运行。");
|
||
}
|
||
string IP = ipp[0];
|
||
int Port = CheckIP(IP, ipp[1]);
|
||
if (Port != 0)
|
||
{
|
||
Socket socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||
NeedConnectSockets.Add(new SocketModel()
|
||
{
|
||
Socket = socket,
|
||
SocketIp = IPPort,
|
||
IsConnected = false,
|
||
HostEP = new IPEndPoint(IPAddress.Parse(IP), Port)
|
||
});
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 检查IP地址和端口号是否满足格式
|
||
/// 如果格式错误返回0,否则返回Int格式的端口号
|
||
/// </summary>
|
||
/// <param name="IP"></param>
|
||
/// <param name="Portstr"></param>
|
||
/// <returns></returns>
|
||
private static int CheckIP(string? IP, string? Portstr)
|
||
{
|
||
if (string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(Portstr))
|
||
{
|
||
throw new Exception("传入的IP或者端口存在异常");
|
||
}
|
||
int Port;//int格式的端口号
|
||
try
|
||
{
|
||
Port = Convert.ToInt32(Portstr);
|
||
}
|
||
catch
|
||
{
|
||
return 0;
|
||
}
|
||
string CheckString = "^((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, CheckString);
|
||
if (!IsIPOK || Port < 1 || Port > 65535)
|
||
{
|
||
throw new Exception($"地址:{IP} 的端口号:{Portstr} 格式不正确,必须处理以后才能正常运行。");
|
||
}
|
||
return Port;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接事件,需要调用
|
||
/// </summary>
|
||
public void Connect()
|
||
{
|
||
if (NeedConnectSockets.Count < 1)
|
||
{
|
||
return;
|
||
}
|
||
// 连接 socket
|
||
foreach (SocketModel needConnectSocket in NeedConnectSockets)
|
||
{
|
||
Thread connectSocket = new(() =>
|
||
{
|
||
ConnectSocket(needConnectSocket);
|
||
})
|
||
{
|
||
IsBackground = true
|
||
};
|
||
connectSocket.Start();
|
||
//Task.Factory.StartNew(()=>ConnectSocket(needConnectSocket));
|
||
}
|
||
// 启用重连
|
||
Thread reConnectSocket = new(ReConnectSocket)
|
||
{
|
||
IsBackground = true
|
||
};
|
||
reConnectSocket.Start();
|
||
//Task.Factory.StartNew(ReConnectSocket);
|
||
}
|
||
/// <summary>
|
||
/// 连接 socket
|
||
/// </summary>
|
||
/// <param name="socketInfo"></param>
|
||
private void ConnectSocket(SocketModel socketInfo)
|
||
{
|
||
while (true)//此循环经本人考虑再三后加上,去掉此循环将导致=>在服务端未开启的情况下开启客户端,后面服务端开启,虽然可以通过下方重连连上服务端,但无法接受数据
|
||
{
|
||
try
|
||
{
|
||
SocketConnecting?.Invoke(socketInfo.SocketIp ?? "");//触发连接中事件
|
||
socketInfo.Socket!.Connect(socketInfo.HostEP!);//尝试连接
|
||
SocketOnline?.Invoke(socketInfo.SocketIp ?? "");//触发连接成功事件
|
||
socketInfo.Thread = Thread.CurrentThread;
|
||
socketInfo.IsConnected = true;
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
SocketConnectFail?.Invoke(socketInfo.SocketIp ?? "", ex);//出发连接中事件
|
||
Thread.Sleep(2000);
|
||
}
|
||
}
|
||
Socket socket = socketInfo.Socket; // 拉取地址
|
||
while (true)
|
||
{
|
||
byte[] recvBytes = new byte[1024 * 1024];
|
||
try
|
||
{
|
||
int bytes = socket.Receive(recvBytes, recvBytes.Length, SocketFlags.None);
|
||
if (bytes <= 0) continue;
|
||
GetTcpData?.BeginInvoke(recvBytes, socketInfo.SocketIp ?? "", null, null);
|
||
if (GetTcpData != null) continue;
|
||
string? recvStr = Encod?.GetString(recvBytes, 0, bytes); //此处编码方式请根据服务端发送的编码方式修改对应,否则可能造成部分字符乱码
|
||
if (string.IsNullOrEmpty(recvStr)) continue;
|
||
if (recvStr.Trim().Length > 0)
|
||
{
|
||
GetSocketMessage?.Invoke(recvStr, socketInfo.SocketIp ?? "");
|
||
if (HandleCodeInfo != null)
|
||
{
|
||
ManCode(recvStr, socketInfo.SocketIp ?? ""); //处理条码并触发其他事件
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_ = ex;
|
||
RecevErrEvent?.Invoke(socketInfo.SocketIp ?? "", ex);
|
||
socketInfo.IsConnected = false;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 重新连接 Socket
|
||
/// </summary>
|
||
private void ReConnectSocket()
|
||
{
|
||
Thread.Sleep(5000);
|
||
while (true)
|
||
{
|
||
foreach (SocketModel needConnectSocket in NeedConnectSockets)
|
||
{
|
||
Socket socket = needConnectSocket.Socket!;
|
||
Thread.Sleep(300);
|
||
try
|
||
{
|
||
try
|
||
{
|
||
socket.SendTimeout = 1000;
|
||
socket.Send(Encoding.ASCII.GetBytes(" "));
|
||
if (!socket.Connected)
|
||
{
|
||
needConnectSocket.IsConnected = false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_ = ex.ToString();
|
||
SocketOffline?.Invoke(needConnectSocket.SocketIp ?? ""); //触发掉线事件
|
||
try
|
||
{
|
||
socket.Close(); //关闭已经断开的socket
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
_ = exception;
|
||
}
|
||
|
||
Thread connectSocket = new(() =>
|
||
{
|
||
ConnectSocket(needConnectSocket);
|
||
})
|
||
{
|
||
IsBackground = true
|
||
};
|
||
connectSocket.Start();
|
||
//Task.Factory.StartNew(() => ConnectSocket(needConnectSocket));
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_ = ex.ToString();
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 处理收到的条码,并引发事件
|
||
/// </summary>
|
||
/// <param name="code"></param>
|
||
/// <param name="IPAddress"></param>
|
||
private void ManCode(string? code, string? IPAddress)
|
||
{
|
||
//多线程处理,增加程序运行速度
|
||
ThreadPool.QueueUserWorkItem((useless) =>
|
||
{
|
||
List<ScanCodeClass>? scanCodeClasses = SplitCode(code, IPAddress);
|
||
if (scanCodeClasses != default)
|
||
{
|
||
foreach (ScanCodeClass scc in scanCodeClasses)
|
||
{
|
||
HandleCodeInfo?.Invoke(scc);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
/// <summary>
|
||
/// 拆条码,将前缀和条码拆下来
|
||
/// </summary>
|
||
/// <param name="GetCode"></param>
|
||
/// <param name="Ipaddress"></param>
|
||
/// <returns></returns>
|
||
private List<ScanCodeClass>? SplitCode(string? GetCode, string? Ipaddress)
|
||
{
|
||
if (string.IsNullOrEmpty(GetCode) || string.IsNullOrEmpty(Ipaddress))
|
||
{
|
||
return default;
|
||
}
|
||
if (!GetCode.Contains('('))
|
||
{
|
||
return default;
|
||
}
|
||
List<ScanCodeClass> sccs = [];
|
||
string[] GetCodeCuts = GetCode.Split('(');
|
||
foreach (string GetCodeCut in GetCodeCuts)
|
||
{
|
||
if (GetCodeCut == "" || GetCodeCut.Trim().Length == 0 || !GetCodeCut.Contains(')')) { continue; }
|
||
string[] ScanCode = GetCodeCut.Split(')');
|
||
if (ScanCode.Length < 2) { continue; }
|
||
ScanCodeClass scc = new()
|
||
{
|
||
IpAddress = Ipaddress,
|
||
ScanID = Convert.ToInt32(ScanCode[0]),
|
||
StrScanID = ScanCode[0],
|
||
Code = ScanCode[1]
|
||
};
|
||
sccs.Add(scc);
|
||
}
|
||
return sccs;
|
||
}
|
||
|
||
#region 事件
|
||
|
||
public delegate void HandleInfo(ScanCodeClass scc);
|
||
/// <summary>
|
||
/// 条码处理事件,
|
||
/// ScanID或者ScanIDStr为null的是没有前缀的条码
|
||
/// </summary>
|
||
public event HandleInfo? HandleCodeInfo;
|
||
|
||
|
||
|
||
public delegate void GetMessage(string Message, string IPAddress);
|
||
/// <summary>
|
||
/// 条码处理事件,
|
||
/// </summary>
|
||
public event GetMessage? GetSocketMessage;
|
||
|
||
|
||
public delegate void GetData(byte[] data, string IPAddress);
|
||
/// <summary>
|
||
/// 字节返回事件
|
||
/// </summary>
|
||
public event GetData? GetTcpData;
|
||
|
||
|
||
public delegate void OffLine(string IPAddress);
|
||
/// <summary>
|
||
/// 当有地址掉线时触发
|
||
/// </summary>
|
||
public event OffLine? SocketOffline;
|
||
|
||
public delegate void OnLine(string IPAddress);
|
||
/// <summary>
|
||
/// 当地址连接成功时触发
|
||
/// </summary>
|
||
public event OnLine? SocketOnline;
|
||
|
||
public delegate void Connecting(string IPAddress);
|
||
/// <summary>
|
||
/// 当地址连接正在连接时触发
|
||
/// </summary>
|
||
public event Connecting? SocketConnecting;
|
||
|
||
public delegate void ConnectFail(string IPAddress, Exception ex);
|
||
/// <summary>
|
||
/// 当地址连接正在连接时触发
|
||
/// </summary>
|
||
public event ConnectFail? SocketConnectFail;
|
||
|
||
public delegate void RecevErr(string IPAddress, Exception ex);
|
||
/// <summary>
|
||
/// 当地址连接接收数据异常时触发
|
||
/// </summary>
|
||
public event RecevErr? RecevErrEvent;
|
||
#endregion
|
||
|
||
|
||
|
||
} |