ajustes contra corrida

This commit is contained in:
Diego Freitas 2026-03-05 12:18:40 -03:00
parent c03c5b1736
commit e718a3858f
2 changed files with 63 additions and 25 deletions

View File

@ -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

View File

@ -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()