518 lines
18 KiB
C#
518 lines
18 KiB
C#
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; }
|
||
}
|
||
}
|
||
} |