Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
2026-01-05 16:23:46 +08:00

518 lines
18 KiB
C#
Raw 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;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace JoyD.Windows.CS.Toprie
{
/// <summary>
/// UDP通信管理器负责管理所有UDP通信实现请求-响应匹配和状态管理
/// 防止异步数据相互影响
/// </summary>
public class UdpCommunicationManager : IDisposable
{
// 单例实例
private static readonly UdpCommunicationManager _instance = new UdpCommunicationManager();
// 命令队列
private readonly Queue<UdpRequest> _requestQueue = new Queue<UdpRequest>();
// 请求映射表,用于匹配请求和响应
private readonly Dictionary<string, UdpRequest> _activeRequests = new Dictionary<string, UdpRequest>();
// 锁对象,保护共享资源
private readonly object _queueLock = new object();
private readonly object _requestLock = new object();
// UDP客户端实例
private UdpClient _udpClient;
// 工作线程
private Thread _workerThread;
private bool _isRunning;
// 信号量,用于通知工作线程有新请求
private AutoResetEvent _requestAvailable = new AutoResetEvent(false);
// 默认端口
private const int DEFAULT_UDP_PORT = 18890;
// 请求超时时间(毫秒)
private const int DEFAULT_TIMEOUT = 500;
/// <summary>
/// 获取UdpCommunicationManager的单例实例
/// </summary>
public static UdpCommunicationManager Instance => _instance;
/// <summary>
/// 私有构造函数初始化UDP通信管理器
/// </summary>
private UdpCommunicationManager()
{
Initialize();
}
/// <summary>
/// 初始化UDP通信管理器
/// </summary>
private void Initialize()
{
try
{
// 创建UDP客户端
_udpClient = new UdpClient();
_udpClient.Client.ReceiveTimeout = DEFAULT_TIMEOUT;
// 启动工作线程
_isRunning = true;
_workerThread = new Thread(ProcessRequests) { IsBackground = true };
_workerThread.Start();
Console.WriteLine("UDP通信管理器已初始化");
}
catch (Exception ex)
{
Console.WriteLine($"UDP通信管理器初始化失败: {ex.Message}");
}
}
/// <summary>
/// 处理请求队列的工作线程函数
/// </summary>
private void ProcessRequests()
{
while (_isRunning)
{
// 等待请求可用信号
_requestAvailable.WaitOne();
if (!_isRunning) break;
UdpRequest request = null;
// 从队列中获取请求
lock (_queueLock)
{
if (_requestQueue.Count > 0)
{
request = _requestQueue.Dequeue();
}
}
if (request != null)
{
// 处理请求
ProcessRequest(request);
}
}
}
/// <summary>
/// 处理单个UDP请求
/// </summary>
/// <param name="request">UDP请求对象</param>
private void ProcessRequest(UdpRequest request)
{
try
{
Console.WriteLine($"处理UDP请求: {request.RequestId}, 目标: {request.TargetIp}:{request.Port}");
// 将请求添加到活动请求列表
lock (_requestLock)
{
_activeRequests[request.RequestId] = request;
}
// 发送UDP数据报
_udpClient.Send(request.Data, request.Data.Length, request.TargetIp, request.Port);
Console.WriteLine($"UDP命令已发送: {Encoding.ASCII.GetString(request.Data)}");
// 启动异步接收响应
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
IAsyncResult ar = _udpClient.BeginReceive(result =>
{
try
{
byte[] responseData = _udpClient.EndReceive(result, ref remoteEndPoint);
HandleResponse(request.RequestId, responseData);
}
catch (Exception ex)
{
Console.WriteLine($"接收UDP响应异常: {ex.Message}");
HandleTimeout(request.RequestId);
}
}, null);
// 设置超时处理(兼容.NET Framework 4.0
ThreadPool.QueueUserWorkItem(state =>
{
string requestId = (string)state;
Thread.Sleep(request.Timeout);
HandleTimeout(requestId);
}, request.RequestId);
}
catch (Exception ex)
{
Console.WriteLine($"处理UDP请求异常: {ex.Message}");
request.OnResponse?.Invoke(null, true); // 通知请求失败
// 从活动请求列表中移除
lock (_requestLock)
{
_activeRequests.Remove(request.RequestId);
}
}
}
/// <summary>
/// 处理接收到的UDP响应
/// </summary>
/// <param name="requestId">请求ID</param>
/// <param name="responseData">响应数据</param>
private void HandleResponse(string requestId, byte[] responseData)
{
UdpRequest request = null;
// 查找对应的请求
lock (_requestLock)
{
if (_activeRequests.TryGetValue(requestId, out request))
{
_activeRequests.Remove(requestId);
}
}
if (request != null)
{
Console.WriteLine($"收到UDP响应: {requestId}, 长度: {responseData?.Length ?? 0}");
request.OnResponse?.Invoke(responseData, false); // 通知请求成功
}
}
/// <summary>
/// 处理请求超时
/// </summary>
/// <param name="requestId">请求ID</param>
private void HandleTimeout(string requestId)
{
UdpRequest request = null;
// 查找对应的请求
lock (_requestLock)
{
if (_activeRequests.TryGetValue(requestId, out request))
{
_activeRequests.Remove(requestId);
}
}
if (request != null)
{
Console.WriteLine($"UDP请求超时: {requestId}");
request.OnResponse?.Invoke(null, true); // 通知请求超时
}
}
/// <summary>
/// 发送UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>包含响应数据的Task</returns>
public Task<byte[]> SendRequestAsync(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
var tcs = new TaskCompletionSource<byte[]>();
string requestId = Guid.NewGuid().ToString();
var request = new UdpRequest
{
RequestId = requestId,
TargetIp = targetIp,
Port = port,
Data = data,
Timeout = timeout,
OnResponse = (response, isTimeout) =>
{
if (isTimeout)
{
tcs.TrySetResult(null);
}
else
{
tcs.TrySetResult(response);
}
}
};
// 将请求添加到队列
lock (_queueLock)
{
_requestQueue.Enqueue(request);
}
// 通知工作线程有新请求
_requestAvailable.Set();
return tcs.Task;
}
/// <summary>
/// UDP请求结果枚举
/// </summary>
public enum RequestResult
{
/// <summary>
/// 请求成功
/// </summary>
Success,
/// <summary>
/// 请求超时
/// </summary>
Timeout,
/// <summary>
/// 网络错误
/// </summary>
NetworkError,
/// <summary>
/// 处理错误
/// </summary>
ProcessingError,
/// <summary>
/// 无效响应
/// </summary>
InvalidResponse
}
/// <summary>
/// 使用独立UDP客户端发送请求并接收响应即用即销毁模式
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>响应数据如果超时或出错则返回null</returns>
private byte[] SendRequestWithDisposableUdp(string targetIp, byte[] data, int port, int timeout)
{
using (UdpClient udpClient = new UdpClient())
{
try
{
// 设置超时时间
udpClient.Client.ReceiveTimeout = timeout;
// 发送数据
IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Parse(targetIp), port);
udpClient.Send(data, data.Length, targetEndPoint);
// 接收响应
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] responseData = udpClient.Receive(ref remoteEndPoint);
return responseData;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine($"UDP请求执行超时({timeout}ms)");
}
else
{
Console.WriteLine($"UDP通信错误: {ex.Message}");
}
return null;
}
catch (Exception ex)
{
Console.WriteLine($"发送UDP请求时发生错误: {ex.Message}");
return null;
}
}
}
/// <summary>
/// 同步发送UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <param name="response">返回的响应数据</param>
/// <returns>请求结果</returns>
public RequestResult SendRequest(string targetIp, byte[] data, out byte[] response, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
response = null;
try
{
// 使用新的即用即销毁UDP方法
response = SendRequestWithDisposableUdp(targetIp, data, port, timeout);
if (response != null)
{
return RequestResult.Success;
}
// 如果响应为null可能是超时或网络错误
// 在SendRequestWithDisposableUdp方法中已经记录了具体错误
return RequestResult.NetworkError;
}
catch (Exception ex)
{
Console.WriteLine($"处理UDP请求结果时发生错误: {ex.Message}");
return RequestResult.ProcessingError;
}
}
/// <summary>
/// 同步发送UDP请求兼容旧版本
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>响应数据如果超时则返回null</returns>
public byte[] SendRequest(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
SendRequest(targetIp, data, out byte[] response, port, timeout);
return response;
}
/// <summary>
/// 异步发送字符串命令的UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="command">要发送的命令字符串</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>包含响应字符串的Task</returns>
public Task<string> SendRequestAsync(string targetIp, string command, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
byte[] data = Encoding.ASCII.GetBytes(command);
Task<byte[]> dataTask = SendRequestAsync(targetIp, data, port, timeout);
// 创建一个新的Task用于返回字符串结果
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
dataTask.ContinueWith(task => {
if (task.IsFaulted)
{
tcs.SetException(task.Exception);
}
else if (task.IsCanceled)
{
tcs.SetCanceled();
}
else
{
byte[] result = task.Result;
string stringResult = result != null ? Encoding.ASCII.GetString(result) : null;
tcs.SetResult(stringResult);
}
});
return tcs.Task;
}
/// <summary>
/// 同步发送字符串命令的UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="command">要发送的命令</param>
/// <param name="timeoutMs">超时时间(毫秒)</param>
/// <param name="response">返回的响应内容</param>
/// <returns>请求结果</returns>
public RequestResult SendRequest(string targetIp, string command, int timeoutMs, out string response)
{
response = null;
try
{
var task = SendRequestAsync(targetIp, command, DEFAULT_UDP_PORT, timeoutMs);
bool completed = task.Wait(timeoutMs + 50); // 额外添加50ms作为缓冲
if (completed)
{
response = task.Result;
if (response != null)
{
// 验证响应格式是否符合预期
if (response.Length > 0 && (response.StartsWith("+RET:") || response.Contains(":")))
{
return RequestResult.Success;
}
Console.WriteLine($"命令 '{command}' 收到无效响应: '{response}'");
return RequestResult.InvalidResponse;
}
return RequestResult.NetworkError;
}
Console.WriteLine($"命令 '{command}' 执行超时({timeoutMs}ms)");
return RequestResult.Timeout;
}
catch (TimeoutException)
{
Console.WriteLine($"命令 '{command}' 执行超时异常");
return RequestResult.Timeout;
}
catch (Exception ex)
{
Console.WriteLine($"发送命令 '{command}' 时发生错误: {ex.Message}");
return RequestResult.ProcessingError;
}
}
/// <summary>
/// 清理资源
/// </summary>
public void Dispose()
{
_isRunning = false;
_requestAvailable.Set(); // 唤醒工作线程以便它可以退出
if (_workerThread != null && _workerThread.IsAlive)
{
_workerThread.Join(1000); // 等待工作线程退出
}
if (_udpClient != null)
{
_udpClient.Close();
_udpClient = null;
}
if (_requestAvailable != null)
{
_requestAvailable.Dispose();
_requestAvailable = null;
}
// 清空请求队列和活动请求
lock (_queueLock)
{
_requestQueue.Clear();
}
lock (_requestLock)
{
_activeRequests.Clear();
}
Console.WriteLine("UDP通信管理器已释放");
}
/// <summary>
/// UDP请求类封装单个UDP请求的信息
/// </summary>
private class UdpRequest
{
public string RequestId { get; set; }
public string TargetIp { get; set; }
public int Port { get; set; }
public byte[] Data { get; set; }
public int Timeout { get; set; }
public Action<byte[], bool> OnResponse { get; set; }
}
}
}