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> _callbacks = new ConcurrentDictionary>(); private bool _running; private const int FixedId = 7; public event Action 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("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("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 callback = null, string requestId = null) { if (requestId == null) requestId = $"{payload.Value("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(); } }