ajustes contra corrida
This commit is contained in:
parent
c03c5b1736
commit
e718a3858f
|
|
@ -21,6 +21,7 @@ using System.Diagnostics;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using AgroMonitor;
|
using AgroMonitor;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
namespace AgroBase.Models
|
namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
|
|
@ -329,7 +330,22 @@ namespace AgroBase.Models
|
||||||
|
|
||||||
if (ms > UDP_RESTART_MS)
|
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
|
finally
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ public sealed class UdpReliableChannel : IDisposable
|
||||||
private CancellationTokenSource _hbCts;
|
private CancellationTokenSource _hbCts;
|
||||||
private Task _hbTask;
|
private Task _hbTask;
|
||||||
private int _hbRunning = 0;
|
private int _hbRunning = 0;
|
||||||
|
private readonly object _sockLock = new object();
|
||||||
|
private volatile bool _stopping = false;
|
||||||
|
|
||||||
|
|
||||||
public void SetRemote(string ip, int port)
|
public void SetRemote(string ip, int port)
|
||||||
|
|
@ -71,37 +73,37 @@ public sealed class UdpReliableChannel : IDisposable
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_running = false;
|
lock (_sockLock)
|
||||||
|
{
|
||||||
|
_stopping = true;
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pare heartbeat antes de matar socket
|
||||||
StopHeartbeat();
|
StopHeartbeat();
|
||||||
|
|
||||||
try { _udp?.Close(); } catch { } // força o Receive quebrar
|
try { _udp?.Close(); } catch { }
|
||||||
try { _udp?.Dispose(); } catch { } // garante liberar socket
|
try { _udp?.Dispose(); } catch { }
|
||||||
|
|
||||||
// espera thread encerrar (pra não ficar thread velha viva)
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_rxThread != null && _rxThread.IsAlive)
|
if (_rxThread != null && _rxThread.IsAlive)
|
||||||
{
|
_rxThread.Join(500);
|
||||||
if (!_rxThread.Join(500))
|
|
||||||
{
|
|
||||||
// Se travar, não aborta (Thread.Abort é treta), só segue.
|
|
||||||
// Mas normalmente Close() já libera.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
_udp = null;
|
lock (_sockLock)
|
||||||
_rxThread = null;
|
{
|
||||||
|
_udp = null;
|
||||||
|
_rxThread = null;
|
||||||
|
_lastSeqRx = 0;
|
||||||
|
|
||||||
// reseta sequência RX pra evitar descartar tudo após restart
|
foreach (var kv in _pendingAck)
|
||||||
_lastSeqRx = 0;
|
kv.Value.TrySetResult(false);
|
||||||
|
_pendingAck.Clear();
|
||||||
|
|
||||||
// opcional: limpa ACKs pendentes pra não vazar
|
_stopping = false;
|
||||||
foreach (var kv in _pendingAck)
|
}
|
||||||
kv.Value.TrySetResult(false);
|
|
||||||
_pendingAck.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
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)
|
private Task SendInternalAsync(byte type, byte[] payload, uint seq, uint tsMs, bool requestAck)
|
||||||
{
|
{
|
||||||
if (_udp == null) throw new InvalidOperationException("Channel not started.");
|
UdpClient udp;
|
||||||
if (Remote == null) throw new InvalidOperationException("Remote not set.");
|
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)
|
if (payload.Length > MaxPayloadBytes)
|
||||||
throw new ArgumentException("Payload too big (" + 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 flags = (byte)(requestAck ? 1 : 0);
|
||||||
|
|
||||||
byte[] buf = new byte[12 + payload.Length];
|
byte[] buf = new byte[12 + payload.Length];
|
||||||
// magic BE
|
|
||||||
buf[0] = (byte)(MAGIC >> 8);
|
buf[0] = (byte)(MAGIC >> 8);
|
||||||
buf[1] = (byte)(MAGIC & 0xFF);
|
buf[1] = (byte)(MAGIC & 0xFF);
|
||||||
buf[2] = type;
|
buf[2] = type;
|
||||||
|
|
@ -235,8 +247,18 @@ public sealed class UdpReliableChannel : IDisposable
|
||||||
if (payload.Length > 0)
|
if (payload.Length > 0)
|
||||||
Buffer.BlockCopy(payload, 0, buf, 12, payload.Length);
|
Buffer.BlockCopy(payload, 0, buf, 12, payload.Length);
|
||||||
|
|
||||||
// UdpClient.SendAsync existe no .NET Framework 4.6+, ok.
|
try
|
||||||
return _udp.SendAsync(buf, buf.Length, Remote);
|
{
|
||||||
|
return udp.SendAsync(buf, buf.Length, remote);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RxLoop()
|
private void RxLoop()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue