From e718a3858ff6338855759cd76af7cddda823e3f6 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Thu, 5 Mar 2026 12:18:40 -0300 Subject: [PATCH] ajustes contra corrida --- AgroBase/AgroBase/Models/Variaveis.cs | 18 ++++- .../AgroBase/Services/UdpReliableChannel.cs | 70 ++++++++++++------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/AgroBase/AgroBase/Models/Variaveis.cs b/AgroBase/AgroBase/Models/Variaveis.cs index 521349c01..228a486f1 100644 --- a/AgroBase/AgroBase/Models/Variaveis.cs +++ b/AgroBase/AgroBase/Models/Variaveis.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using Newtonsoft.Json; using AgroMonitor; using System.Net; +using System.Net.Sockets; namespace AgroBase.Models { @@ -329,7 +330,22 @@ namespace AgroBase.Models if (ms > UDP_RESTART_MS) { - await RestartUdpChannelSafe(); + try + { + await RestartUdpChannelSafe(); + } + catch (SocketException) + { + // normal durante restart + } + catch (ObjectDisposedException) + { + // normal durante restart + } + catch (Exception ex) + { + Console.WriteLine("[UDP] Watchdog exception: " + ex.Message); + } } } finally diff --git a/AgroBase/AgroBase/Services/UdpReliableChannel.cs b/AgroBase/AgroBase/Services/UdpReliableChannel.cs index 36f9712d3..fc6d2911b 100644 --- a/AgroBase/AgroBase/Services/UdpReliableChannel.cs +++ b/AgroBase/AgroBase/Services/UdpReliableChannel.cs @@ -38,6 +38,8 @@ public sealed class UdpReliableChannel : IDisposable private CancellationTokenSource _hbCts; private Task _hbTask; private int _hbRunning = 0; + private readonly object _sockLock = new object(); + private volatile bool _stopping = false; public void SetRemote(string ip, int port) @@ -71,37 +73,37 @@ public sealed class UdpReliableChannel : IDisposable public void Stop() { - _running = false; + lock (_sockLock) + { + _stopping = true; + _running = false; + } + // pare heartbeat antes de matar socket StopHeartbeat(); - try { _udp?.Close(); } catch { } // força o Receive quebrar - try { _udp?.Dispose(); } catch { } // garante liberar socket + try { _udp?.Close(); } catch { } + try { _udp?.Dispose(); } catch { } - // espera thread encerrar (pra não ficar thread velha viva) try { if (_rxThread != null && _rxThread.IsAlive) - { - if (!_rxThread.Join(500)) - { - // Se travar, não aborta (Thread.Abort é treta), só segue. - // Mas normalmente Close() já libera. - } - } + _rxThread.Join(500); } catch { } - _udp = null; - _rxThread = null; + lock (_sockLock) + { + _udp = null; + _rxThread = null; + _lastSeqRx = 0; - // reseta sequência RX pra evitar descartar tudo após restart - _lastSeqRx = 0; + foreach (var kv in _pendingAck) + kv.Value.TrySetResult(false); + _pendingAck.Clear(); - // opcional: limpa ACKs pendentes pra não vazar - foreach (var kv in _pendingAck) - kv.Value.TrySetResult(false); - _pendingAck.Clear(); + _stopping = false; + } } public void Dispose() @@ -214,8 +216,19 @@ public sealed class UdpReliableChannel : IDisposable private Task SendInternalAsync(byte type, byte[] payload, uint seq, uint tsMs, bool requestAck) { - if (_udp == null) throw new InvalidOperationException("Channel not started."); - if (Remote == null) throw new InvalidOperationException("Remote not set."); + UdpClient udp; + IPEndPoint remote; + + lock (_sockLock) + { + if (_stopping || !_running) return Task.CompletedTask; + + udp = _udp; + remote = Remote; + } + + if (udp == null) return Task.CompletedTask; + if (remote == null) return Task.CompletedTask; if (payload.Length > MaxPayloadBytes) throw new ArgumentException("Payload too big (" + payload.Length + " > " + MaxPayloadBytes + ")."); @@ -223,7 +236,6 @@ public sealed class UdpReliableChannel : IDisposable byte flags = (byte)(requestAck ? 1 : 0); byte[] buf = new byte[12 + payload.Length]; - // magic BE buf[0] = (byte)(MAGIC >> 8); buf[1] = (byte)(MAGIC & 0xFF); buf[2] = type; @@ -235,8 +247,18 @@ public sealed class UdpReliableChannel : IDisposable if (payload.Length > 0) Buffer.BlockCopy(payload, 0, buf, 12, payload.Length); - // UdpClient.SendAsync existe no .NET Framework 4.6+, ok. - return _udp.SendAsync(buf, buf.Length, Remote); + try + { + return udp.SendAsync(buf, buf.Length, remote); + } + catch (ObjectDisposedException) + { + return Task.CompletedTask; + } + catch (SocketException) + { + return Task.CompletedTask; + } } private void RxLoop()