UDP CLIENT
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, }}