171 lines
5.5 KiB
C#
171 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace YKC
|
|
{
|
|
public class UdpClient : IDisposable
|
|
{
|
|
private Socket _sock;
|
|
private readonly object _lock = new object();
|
|
private readonly ConcurrentDictionary<string, Action<JObject>> _callbacks = new ConcurrentDictionary<string, Action<JObject>>();
|
|
private bool _running;
|
|
private const int FixedId = 7;
|
|
|
|
public event Action<JObject> OnActiveReport;
|
|
|
|
public UdpClient()
|
|
{
|
|
try
|
|
{
|
|
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
_sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
_sock.Bind(new IPEndPoint(IPAddress.Any, Config.LocalPort));
|
|
_sock.ReceiveTimeout = 1000;
|
|
_running = true;
|
|
Console.WriteLine($"[UDP] 监听端口 {Config.LocalPort}");
|
|
Task.Run(() => ListenLoop());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"[UDP] 初始化失败: {e.Message}");
|
|
}
|
|
}
|
|
|
|
private void ListenLoop()
|
|
{
|
|
var buffer = new byte[4096];
|
|
EndPoint remoteEp = new IPEndPoint(IPAddress.Any, 0);
|
|
|
|
while (_running)
|
|
{
|
|
try
|
|
{
|
|
int recv = _sock.ReceiveFrom(buffer, ref remoteEp);
|
|
string json = Encoding.UTF8.GetString(buffer, 0, recv);
|
|
var msg = JObject.Parse(json);
|
|
Dispatch(msg);
|
|
}
|
|
catch (SocketException) { }
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"[UDP] 接收异常: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Dispatch(JObject msg)
|
|
{
|
|
string reqId = msg.Value<string>("request_id");
|
|
Console.WriteLine($"[UDP] 收到回复: {msg}");
|
|
|
|
if (reqId != null && _callbacks.TryRemove(reqId, out var cb))
|
|
{
|
|
try { cb(msg); }
|
|
catch (Exception e) { Console.WriteLine($"[UDP] 回调异常: {e.Message}"); }
|
|
}
|
|
else
|
|
{
|
|
HandleActiveReport(msg);
|
|
}
|
|
}
|
|
|
|
private void HandleActiveReport(JObject msg)
|
|
{
|
|
string cmd = msg.Value<string>("cmd");
|
|
if (cmd == "report_data" || cmd == "pile_metrics" || cmd == "real_time_data")
|
|
{
|
|
Console.WriteLine($"[UDP] 收到主动上报, cmd={cmd}");
|
|
OnActiveReport?.Invoke(msg);
|
|
}
|
|
}
|
|
|
|
public void Send(JObject payload, Action<JObject> callback = null, string requestId = null)
|
|
{
|
|
if (requestId == null)
|
|
requestId = $"{payload.Value<string>("cmd")}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
|
|
|
payload["id"] = FixedId;
|
|
payload["request_id"] = requestId;
|
|
|
|
if (callback != null)
|
|
{
|
|
_callbacks[requestId] = callback;
|
|
StartTimeoutCleaner(requestId);
|
|
}
|
|
|
|
try
|
|
{
|
|
if (_sock == null)
|
|
{
|
|
Console.WriteLine("[UDP] 套接字未就绪");
|
|
callback?.Invoke(new JObject { ["success"] = false, ["error"] = "socket_not_ready", ["request_id"] = requestId });
|
|
return;
|
|
}
|
|
byte[] data = Encoding.UTF8.GetBytes(payload.ToString(Formatting.None));
|
|
_sock.SendTo(data, new IPEndPoint(IPAddress.Parse(Config.TargetIp), Config.TargetPort));
|
|
Console.WriteLine($"[UDP] 发送: {payload}");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"[UDP] 发送失败: {e.Message}");
|
|
_callbacks.TryRemove(requestId, out _);
|
|
}
|
|
}
|
|
|
|
public JObject SendSync(JObject payload, int timeoutSec = 4)
|
|
{
|
|
JObject result = null;
|
|
var evt = new ManualResetEvent(false);
|
|
|
|
Send(payload, (msg) =>
|
|
{
|
|
result = msg;
|
|
evt.Set();
|
|
});
|
|
|
|
evt.WaitOne(TimeSpan.FromSeconds(timeoutSec));
|
|
return result ?? new JObject { ["success"] = false, ["error"] = "设备无响应" };
|
|
}
|
|
|
|
private void StartTimeoutCleaner(string requestId)
|
|
{
|
|
Task.Run(async () =>
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(Config.UdpTimeout));
|
|
if (_callbacks.TryRemove(requestId, out var cb))
|
|
{
|
|
Console.WriteLine($"[UDP] 请求超时: {requestId}");
|
|
try
|
|
{
|
|
cb(new JObject
|
|
{
|
|
["success"] = false,
|
|
["error"] = "timeout",
|
|
["request_id"] = requestId
|
|
});
|
|
}
|
|
catch { }
|
|
}
|
|
});
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_running = false;
|
|
_sock?.Close();
|
|
}
|
|
}
|
|
|
|
public static class UdpClientHolder
|
|
{
|
|
public static readonly UdpClient Instance = new UdpClient();
|
|
}
|
|
}
|