using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Aes.UdpTester {
public partial class Form1 : Form {
private AesUdpClient _udpClient = new AesUdpClient("120.79.232.38", 12018, null);
public Form1() {
InitializeComponent();
_udpClient.ReceiveData += _udpClient_ReceiveData;
_udpClient.Start();
}
private List<byte[]> _dataRcv = new List<byte[]>();
private void _udpClient_ReceiveData(object sender, ReceiveDataEventArgs e) {
_dataRcv.Add(e.Data);
}
private void button1_Click(object sender, EventArgs e) {
var txt = txt_Msg.Text.Trim();
if (string.IsNullOrEmpty(txt)) return;
var bytes = Encoding.UTF8.GetBytes(txt);
_udpClient.SendBytes(bytes);
}
private void timer1_Tick(object sender, EventArgs e) {
if (_dataRcv.Count == 0) return;
_dataRcv.ForEach(a => {
txt_Logs.AppendText($"[{DateTime.Now:HH:mm:ss fff}]RCV:{a.Length} bytes" + Environment.NewLine);
});
_dataRcv.Clear();
}
}
public abstract class NetClientBase : INetClient {
/// <summary>
/// 连接的远程服务器Ip地址
/// </summary>
public string ServerIp { get; }
/// <summary>
/// 连接的远程服务器端口号
/// </summary>
public int ServerPort { get; }
/// <summary>
/// 网络状态
/// </summary>
public NetState State { get; protected set; }
public abstract void SendBytes(byte[] bytes);
/// <summary>
///
/// </summary>
public BuffBlock Buffer { get; }
/// <summary>
/// 报文处理器
/// </summary>
public INetHandler Handler { get; }
protected IPEndPoint Remote { get; private set; }
/// <summary>
///
/// </summary>
protected NetClientBase(string ip, int port, INetHandler handler) {
ServerIp = ip;
ServerPort = port;
Handler = handler;
Remote = new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort);
Buffer = new BuffBlock();
}
/// <summary>
///
/// </summary>
protected virtual void Init() { }
}
public interface INetHandler {
/// <summary>
/// 处理接收到的报文
/// </summary>
int Handler(ArraySegment<byte> segment);
/// <summary>
///
/// </summary>
void HandleTryConnect();
/// <summary>
///
/// </summary>
void HandleConnectSuccess();
/// <summary>
///
/// </summary>
void HandleConnectFailure();
/// <summary>
///
/// </summary>
void HandleDisconnect();
}
/// <summary>
///
/// </summary>
public class AesUdpClient : NetClientBase {
//Sae池
private static readonly SaeQueuePool _saeQueue = new SaeQueuePool(1024);
private Socket _listen;
private readonly int _bufferLength;
/// <summary>
/// 获取SEA池中的可用项数量
/// </summary>
public int SeaCount => _saeQueue.Count;
/// <summary>
/// 获取系统分配的UDP端口号
/// </summary>
public int LocalPort { get; private set; }
/// <summary>
/// </summary>
public long ReceivedBytesCount { get; private set; }
/// <summary>
/// </summary>
public long SendBytesCount { get; private set; }
/// <summary>
/// 收到的报文数量
/// </summary>
public uint ReceivedCount { get; private set; }
/// <summary>
/// 发送的报文数量
/// </summary>
public uint SendCount { get; private set; }
/// <summary>
/// </summary>
public bool IsRunning { get; private set; }
/// <summary>
///
/// </summary>
public AesUdpClient(string ip, int port, INetHandler handler) : base(ip, port, handler) {
_bufferLength = 1024;
}
/// <summary>
///
/// </summary>
public override void SendBytes(byte[] bytes) {
if (!IsRunning || _listen == null)
return;
//优先从可复用队列中取
var socketSendAsync = _saeQueue.Dequeue();
socketSendAsync.SetBuffer(bytes, 0, bytes.Length);
socketSendAsync.RemoteEndPoint = Remote;
SendCount++;
SendBytesCount += bytes.Length;
//Counter.Udp_SendBytesCount += bytes.Length;
//LogConsole.Debug($"[UDP]Send={bytes.ToHexString()}");
//如果发送失败,则强制归队
if (!_listen.SendToAsync(socketSendAsync))
_saeQueue.Enqueue(socketSendAsync);
}
/// <summary>
/// 收到数据事件
/// </summary>
public event EventHandler<ReceiveDataEventArgs> ReceiveData;
/// <summary>
/// 启动UPD监听,接收来自网络上的UDP消息
/// </summary>
public void Start() {
_listen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_listen.Connect(Remote);
//_listen.Bind(local);
var socketAsync = new SocketAsyncEventArgs();
socketAsync.SetBuffer(new byte[_bufferLength], 0, _bufferLength);
socketAsync.Completed += SocketAsync_Completed;
socketAsync.RemoteEndPoint = Remote;
IsRunning = true;
StartReceive(socketAsync);
}
/// <summary>
/// </summary>
public void Stop() {
IsRunning = false;
_listen?.Close();
_listen = null;
}
/// <summary>
/// 重置收发数据统计
/// </summary>
public void ResetSR() {
ReceivedBytesCount = 0;
SendBytesCount = 0;
ReceivedCount = 0;
SendCount = 0;
}
/// <summary>
/// 开始异步接收UDP消息
/// </summary>
private void StartReceive(SocketAsyncEventArgs socketAsync) {
if (!IsRunning) return;
//https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.socket.receivefromasync?view=netframework-4.8
//ReceiveFromAsync方法主要用于接收无连接套接字上的数据。 套接字的本地地址必须是已知的。
//调用方必须将 SocketAsyncEventArgs.RemoteEndPoint 属性设置为从中 IPEndPoint 接收数据的远程主机的。
//SocketAsyncEventArgs.SocketFlags参数上的属性为 e 窗口套接字服务提供程序提供有关读取请求的其他信息。
//有关如何使用此参数的详细信息,请参阅 System.Net.Sockets.SocketFlags 。
//如果 I/O 操作挂起,则为 true。 操作完成时,将引发 e 参数的 Completed 事件。
//如果 I/ O 操作同步完成,则为 false。
//在这种情况下,将不会引发 e 参数的 Completed 事件,并且可能在方法调用返回后立即检查作为参数传递的 e 对象以检索操作的结果。
if (!_listen.ReceiveFromAsync(socketAsync))
SocketAsync_Completed(_listen, socketAsync);
}
/// <summary>
/// </summary>
private void SocketAsync_Completed(object sender, SocketAsyncEventArgs e) {
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
if (e.LastOperation == SocketAsyncOperation.ReceiveFrom) {
var data = new byte[e.BytesTransferred];
System.Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, data.Length);
//LogConsole.Debug($"[UDP]Rcv:{data.ToHexString()},长度={data.Length}");
ReceivedBytesCount += e.BytesTransferred;
ReceivedCount++;
StartReceive(e);
OnReceive((IPEndPoint)e.RemoteEndPoint, data);
return;
}
StartReceive(e);
}
/// <summary>
/// 远程端点收到消息处理
/// </summary>
private void OnReceive(IPEndPoint iPEndPoint, byte[] data) {
if (!IsRunning) return;
if (iPEndPoint == null || data == null || data.Length == 0) return;
//Counter.Udp_RecvBytesCount += data.Length;
//抛出收到数据事件
ReceiveData?.Invoke(this, new ReceiveDataEventArgs {
Data = data, IpEndPoint = iPEndPoint
});
return;
if (data.Length < 8 + 4) {
//TODO:执行缓冲
#if DEBUG
//LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文过短,抛弃:" + data.ToHexString());
#endif
return;
}
var i = 0;
while (i < data.Length) {
var prefix = data[i];
if (prefix != 0x7E) {
#if DEBUG
//LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文首字错误,抛弃:" + data.ToHexString());
#endif
return;
}
var bodyLen = BitConverter.ToUInt16(data, i + 3);
if (bodyLen <= 0) return;
var len = 5 + bodyLen;
var subfix = data[i + len - 1];
if (subfix != 0x7F) {
#if DEBUG
//LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文尾字错误,抛弃:" + data.ToHexString());
#endif
return;
}
var d = new byte[len];
Array.Copy(data, i, d, 0, len);
// _handlerQueue.Enqueue(new UdpRequestWrapper(d, iPEndPoint));
//#if DEBUG
// if (Settings.DebugMode && Settings.DebugUdpPacket)
// LogConsole.Debug($"[UDP]{iPEndPoint.Address}偏移={i},解析获得报文:{d.ToHexString()}");
//#endif
i += len;
}
}
}
/// <summary>
/// 接收数据事件参数
/// </summary>
public class ReceiveDataEventArgs : EventArgs {
/// <summary>
/// 收到的数据
/// </summary>
public byte[] Data { get; set; }
/// <summary>
/// 发送数据的IP
/// </summary>
public IPEndPoint IpEndPoint { get; set; }
}
/// <summary>
/// SocketAsyncEventArgs可复用队列
/// </summary>
internal class SaeQueuePool {
/// <summary>
/// SocketAsyncEventArgs可复用队列
/// </summary>
private readonly ConcurrentQueue<SocketAsyncEventArgs> _saeQueue;
/// <summary>
/// 构造函数
/// </summary>
public SaeQueuePool(int count) {
_saeQueue = new ConcurrentQueue<SocketAsyncEventArgs>();
for (var i = 0; i < count; i++) _saeQueue.Enqueue(new SocketAsyncEventArgs());
}
/// <summary>
/// 获取队列中的数量
/// </summary>
public int Count => _saeQueue.Count;
/// <summary>
/// 取出项
/// </summary>
public SocketAsyncEventArgs Dequeue() {
var success = _saeQueue.TryDequeue(out var item);
if (!success)
item = new SocketAsyncEventArgs();
item.Completed += Item_Completed;
return item;
}
/// <summary>
/// </summary>
private void Item_Completed(object sender, SocketAsyncEventArgs e) {
e.Completed -= Item_Completed;
_saeQueue.Enqueue(e);
}
/// <summary>
/// 退回项到池中
/// </summary>
public void Enqueue(SocketAsyncEventArgs sae) {
sae.Completed -= Item_Completed;
_saeQueue.Enqueue(sae);
}
}
/// <summary>
///
/// </summary>
public class BuffBlock {
private readonly byte[] _buff;
public int Size { get; }
public int OffsetFree => Start + Count;
/// <summary>
///
/// </summary>
public int Count { get; private set; }
/// <summary>
///
/// </summary>
public int Start { get; private set; }
public BuffBlock(int size = 1024 * 20) {
Size = size;
_buff = new byte[Size];
}
public void Append(byte[] data, int offset, int count) {
if (OffsetFree + count > Size)
Recalc();
try {
Array.Copy(data, offset, _buff, OffsetFree, count);
Count += count;
}
catch (Exception ex) {
}
}
public void Take(int count) {
if (count > Count)
throw new ArgumentOutOfRangeException();
Start += count;
Count -= count;
}
private void Recalc() {
if (Count == 0) {
Start = 0;
return;
}
Array.Copy(_buff, Start, _buff, 0, Count);
Start = 0;
}
public ArraySegment<byte> GetBytes() {
return new ArraySegment<byte>(_buff, Start, Count);
}
public void Clear() {
Start = 0;
Count = 0;
}
}
/// <summary>
///
/// </summary>
public interface INetClient {
/// <summary>
///
/// </summary>
string ServerIp { get; }
/// <summary>
///
/// </summary>
int ServerPort { get; }
/// <summary>
///
/// </summary>
NetState State { get; }
/// <summary>
///
/// </summary>
void SendBytes(byte[] bytes);
}
public interface ITcpNetClient : INetClient {
/// <summary>
///
/// </summary>
void TryConnectAsyn();
/// <summary>
///
/// </summary>
void DisConnect();
}
/// <summary>
///
/// </summary>
public enum NetState {
/// <summary>
/// 未连接或已断线状态
/// </summary>
None,
/// <summary>
/// 正在连接,此时不允许再次发起连接动作
/// </summary>
Connectting,
/// <summary>
/// 正常工作状态
/// </summary>
Working,
}
}
using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;
namespace Aes.UdpTester { public partial class Form1 : Form { private AesUdpClient _udpClient = new AesUdpClient("120.79.232.38", 12018, null);
public Form1() { InitializeComponent(); _udpClient.ReceiveData += _udpClient_ReceiveData; _udpClient.Start(); }
private List<byte[]> _dataRcv = new List<byte[]>(); private void _udpClient_ReceiveData(object sender, ReceiveDataEventArgs e) { _dataRcv.Add(e.Data); }
private void button1_Click(object sender, EventArgs e) { var txt = txt_Msg.Text.Trim(); if (string.IsNullOrEmpty(txt)) return;
var bytes = Encoding.UTF8.GetBytes(txt);
_udpClient.SendBytes(bytes); }
private void timer1_Tick(object sender, EventArgs e) { if (_dataRcv.Count == 0) return;
_dataRcv.ForEach(a => { txt_Logs.AppendText($"[{DateTime.Now:HH:mm:ss fff}]RCV:{a.Length} bytes" + Environment.NewLine); });
_dataRcv.Clear(); } }
public abstract class NetClientBase : INetClient { /// <summary> /// 连接的远程服务器Ip地址 /// </summary> public string ServerIp { get; }
/// <summary> /// 连接的远程服务器端口号 /// </summary> public int ServerPort { get; }
/// <summary> /// 网络状态 /// </summary> public NetState State { get; protected set; }
public abstract void SendBytes(byte[] bytes);
/// <summary> /// /// </summary> public BuffBlock Buffer { get; }
/// <summary> /// 报文处理器 /// </summary> public INetHandler Handler { get; }
protected IPEndPoint Remote { get; private set; }
/// <summary> /// /// </summary> protected NetClientBase(string ip, int port, INetHandler handler) { ServerIp = ip; ServerPort = port; Handler = handler;
Remote = new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort); Buffer = new BuffBlock(); }
/// <summary> /// /// </summary> protected virtual void Init() { } }
public interface INetHandler { /// <summary> /// 处理接收到的报文 /// </summary> int Handler(ArraySegment<byte> segment);
/// <summary> /// /// </summary> void HandleTryConnect();
/// <summary> /// /// </summary> void HandleConnectSuccess();
/// <summary> /// /// </summary> void HandleConnectFailure();
/// <summary> /// /// </summary> void HandleDisconnect(); }
/// <summary> /// /// </summary> public class AesUdpClient : NetClientBase { //Sae池 private static readonly SaeQueuePool _saeQueue = new SaeQueuePool(1024); private Socket _listen; private readonly int _bufferLength;
/// <summary> /// 获取SEA池中的可用项数量 /// </summary> public int SeaCount => _saeQueue.Count;
/// <summary> /// 获取系统分配的UDP端口号 /// </summary> public int LocalPort { get; private set; }
/// <summary> /// </summary> public long ReceivedBytesCount { get; private set; }
/// <summary> /// </summary> public long SendBytesCount { get; private set; }
/// <summary> /// 收到的报文数量 /// </summary> public uint ReceivedCount { get; private set; }
/// <summary> /// 发送的报文数量 /// </summary> public uint SendCount { get; private set; }
/// <summary> /// </summary> public bool IsRunning { get; private set; }
/// <summary> /// /// </summary> public AesUdpClient(string ip, int port, INetHandler handler) : base(ip, port, handler) { _bufferLength = 1024; }
/// <summary> /// /// </summary> public override void SendBytes(byte[] bytes) { if (!IsRunning || _listen == null) return;
//优先从可复用队列中取 var socketSendAsync = _saeQueue.Dequeue(); socketSendAsync.SetBuffer(bytes, 0, bytes.Length); socketSendAsync.RemoteEndPoint = Remote;
SendCount++; SendBytesCount += bytes.Length;
//Counter.Udp_SendBytesCount += bytes.Length; //LogConsole.Debug($"[UDP]Send={bytes.ToHexString()}");
//如果发送失败,则强制归队 if (!_listen.SendToAsync(socketSendAsync)) _saeQueue.Enqueue(socketSendAsync); }
/// <summary> /// 收到数据事件 /// </summary> public event EventHandler<ReceiveDataEventArgs> ReceiveData;
/// <summary> /// 启动UPD监听,接收来自网络上的UDP消息 /// </summary> public void Start() { _listen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _listen.Connect(Remote);
//_listen.Bind(local);
var socketAsync = new SocketAsyncEventArgs(); socketAsync.SetBuffer(new byte[_bufferLength], 0, _bufferLength); socketAsync.Completed += SocketAsync_Completed; socketAsync.RemoteEndPoint = Remote;
IsRunning = true;
StartReceive(socketAsync); }
/// <summary> /// </summary> public void Stop() { IsRunning = false; _listen?.Close(); _listen = null; }
/// <summary> /// 重置收发数据统计 /// </summary> public void ResetSR() { ReceivedBytesCount = 0; SendBytesCount = 0; ReceivedCount = 0; SendCount = 0; }
/// <summary> /// 开始异步接收UDP消息 /// </summary> private void StartReceive(SocketAsyncEventArgs socketAsync) { if (!IsRunning) return;
//https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.socket.receivefromasync?view=netframework-4.8 //ReceiveFromAsync方法主要用于接收无连接套接字上的数据。 套接字的本地地址必须是已知的。 //调用方必须将 SocketAsyncEventArgs.RemoteEndPoint 属性设置为从中 IPEndPoint 接收数据的远程主机的。 //SocketAsyncEventArgs.SocketFlags参数上的属性为 e 窗口套接字服务提供程序提供有关读取请求的其他信息。 //有关如何使用此参数的详细信息,请参阅 System.Net.Sockets.SocketFlags 。
//如果 I/O 操作挂起,则为 true。 操作完成时,将引发 e 参数的 Completed 事件。 //如果 I/ O 操作同步完成,则为 false。 //在这种情况下,将不会引发 e 参数的 Completed 事件,并且可能在方法调用返回后立即检查作为参数传递的 e 对象以检索操作的结果。
if (!_listen.ReceiveFromAsync(socketAsync)) SocketAsync_Completed(_listen, socketAsync); }
/// <summary> /// </summary> private void SocketAsync_Completed(object sender, SocketAsyncEventArgs e) { if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) if (e.LastOperation == SocketAsyncOperation.ReceiveFrom) { var data = new byte[e.BytesTransferred]; System.Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, data.Length);
//LogConsole.Debug($"[UDP]Rcv:{data.ToHexString()},长度={data.Length}");
ReceivedBytesCount += e.BytesTransferred; ReceivedCount++;
StartReceive(e); OnReceive((IPEndPoint)e.RemoteEndPoint, data); return; }
StartReceive(e); }
/// <summary> /// 远程端点收到消息处理 /// </summary> private void OnReceive(IPEndPoint iPEndPoint, byte[] data) { if (!IsRunning) return; if (iPEndPoint == null || data == null || data.Length == 0) return;
//Counter.Udp_RecvBytesCount += data.Length;
//抛出收到数据事件 ReceiveData?.Invoke(this, new ReceiveDataEventArgs { Data = data, IpEndPoint = iPEndPoint });
return; if (data.Length < 8 + 4) { //TODO:执行缓冲#if DEBUG //LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文过短,抛弃:" + data.ToHexString());#endif return; }
var i = 0; while (i < data.Length) { var prefix = data[i]; if (prefix != 0x7E) {#if DEBUG //LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文首字错误,抛弃:" + data.ToHexString());#endif return; }
var bodyLen = BitConverter.ToUInt16(data, i + 3); if (bodyLen <= 0) return;
var len = 5 + bodyLen; var subfix = data[i + len - 1]; if (subfix != 0x7F) {#if DEBUG //LogConsole.Debug($"[UDP]RCV={iPEndPoint.Address}报文尾字错误,抛弃:" + data.ToHexString());#endif return; }
var d = new byte[len]; Array.Copy(data, i, d, 0, len);
// _handlerQueue.Enqueue(new UdpRequestWrapper(d, iPEndPoint)); //#if DEBUG // if (Settings.DebugMode && Settings.DebugUdpPacket) // LogConsole.Debug($"[UDP]{iPEndPoint.Address}偏移={i},解析获得报文:{d.ToHexString()}"); //#endif
i += len; } } }
/// <summary> /// 接收数据事件参数 /// </summary> public class ReceiveDataEventArgs : EventArgs { /// <summary> /// 收到的数据 /// </summary> public byte[] Data { get; set; }
/// <summary> /// 发送数据的IP /// </summary> public IPEndPoint IpEndPoint { get; set; } }
/// <summary> /// SocketAsyncEventArgs可复用队列 /// </summary> internal class SaeQueuePool { /// <summary> /// SocketAsyncEventArgs可复用队列 /// </summary> private readonly ConcurrentQueue<SocketAsyncEventArgs> _saeQueue;
/// <summary> /// 构造函数 /// </summary> public SaeQueuePool(int count) { _saeQueue = new ConcurrentQueue<SocketAsyncEventArgs>(); for (var i = 0; i < count; i++) _saeQueue.Enqueue(new SocketAsyncEventArgs()); }
/// <summary> /// 获取队列中的数量 /// </summary> public int Count => _saeQueue.Count;
/// <summary> /// 取出项 /// </summary> public SocketAsyncEventArgs Dequeue() { var success = _saeQueue.TryDequeue(out var item); if (!success) item = new SocketAsyncEventArgs();
item.Completed += Item_Completed; return item; }
/// <summary> /// </summary> private void Item_Completed(object sender, SocketAsyncEventArgs e) { e.Completed -= Item_Completed; _saeQueue.Enqueue(e); }
/// <summary> /// 退回项到池中 /// </summary> public void Enqueue(SocketAsyncEventArgs sae) { sae.Completed -= Item_Completed; _saeQueue.Enqueue(sae); } }
/// <summary> /// /// </summary> public class BuffBlock { private readonly byte[] _buff;
public int Size { get; }
public int OffsetFree => Start + Count;
/// <summary> /// /// </summary> public int Count { get; private set; }
/// <summary> /// /// </summary> public int Start { get; private set; }
public BuffBlock(int size = 1024 * 20) { Size = size; _buff = new byte[Size]; }
public void Append(byte[] data, int offset, int count) { if (OffsetFree + count > Size) Recalc();
try { Array.Copy(data, offset, _buff, OffsetFree, count); Count += count; } catch (Exception ex) {
} }
public void Take(int count) { if (count > Count) throw new ArgumentOutOfRangeException();
Start += count; Count -= count; }
private void Recalc() { if (Count == 0) { Start = 0; return; }
Array.Copy(_buff, Start, _buff, 0, Count); Start = 0; }
public ArraySegment<byte> GetBytes() { return new ArraySegment<byte>(_buff, Start, Count); }
public void Clear() { Start = 0; Count = 0; } }
/// <summary> /// /// </summary> public interface INetClient { /// <summary> /// /// </summary> string ServerIp { get; }
/// <summary> /// /// </summary> int ServerPort { get; }
/// <summary> /// /// </summary> NetState State { get; }
/// <summary> /// /// </summary> void SendBytes(byte[] bytes); }
public interface ITcpNetClient : INetClient { /// <summary> /// /// </summary> void TryConnectAsyn();
/// <summary> /// /// </summary> void DisConnect(); }
/// <summary> /// /// </summary> public enum NetState { /// <summary> /// 未连接或已断线状态 /// </summary> None, /// <summary> /// 正在连接,此时不允许再次发起连接动作 /// </summary> Connectting, /// <summary> /// 正常工作状态 /// </summary> Working, }}