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