fix desync & try to fix delay
All checks were successful
Server Build / publish (push) Successful in 29s
Voice Build / publish (push) Successful in 25s

This commit is contained in:
olcxja 2026-08-26 12:17:10 +02:00
commit dc893baf94
5 changed files with 78 additions and 3 deletions

View file

@ -14,9 +14,44 @@ public class Program
// Entries are pruned in the 1s loop below.
public static ConcurrentDictionary<string, (int count, long resetAt)> UsersListLimits = new ();
// Sesja bez ODBIERANEGO ruchu dłużej niż to = martwy TCP (zabita aplikacja,
// utracony mapping NAT). Klient pinguje co 2 s - 15 s to 7+ przegapionych
// pingów, więc fałszywe pozytywy przy chwilowych zaniżeniach sieci są
// praktycznie niemożliwe, a ghost nie zaśmieca rosteru dłużej niż trzeba.
public static readonly TimeSpan SessionIdleTimeout = TimeSpan.FromSeconds(15);
public static ulong TotalBytesSent = 0;
public static ulong TotalBytesReceived = 0;
private static ulong CurrentBandwidthKps = 0;
// Zamiata sesje bez odebranego ruchu dłużej niż SessionIdleTimeout. Bez
// tego zabita aplikacja (half-open TCP, brak Close frame) zostawała w
// rosterze pokoju na zawsze: /room/users zwracało ghostów -> ghost kafelki
// i "trwa rozmowa" u innych uczestników. TryRemove(KeyValuePair) jest
// warunkowe po wartości - nie da się przypadkiem usunąć NOWEJ sesji, która
// w międzyczasie nadpisała wpis tego samego userId (reconnect).
private static void SweepStaleSessions()
{
DateTime cutoff = DateTime.UtcNow - SessionIdleTimeout;
foreach (var room in Rooms)
{
foreach (var kvp in room.Value)
{
if (kvp.Value.LastSeenUtc >= cutoff) continue;
if (!room.Value.TryRemove(new KeyValuePair<ulong, Session>(kvp.Key, kvp.Value))) continue;
//Zamknięcie kolejki kończy task-pump; Abort wywala zawieszone
//ReceiveAsync/SendAsync martwego socketu -> receive-loop wejdzie
//we swój finally i posprząta bufor (guard session==currentSession
//zapobiega podwójnemu sprzątaniu)
kvp.Value.SendQueue.Writer.TryComplete();
try { kvp.Value.Socket.Abort(); } catch { }
Console.WriteLine($"Voice: swept idle session room={room.Key} user={kvp.Key}");
}
if (room.Value.IsEmpty) Rooms.TryRemove(room.Key, out _);
}
}
public static void Main(string[] args)
{
@ -39,6 +74,8 @@ public class Program
{
if (entry.Value.resetAt <= nowSec) UsersListLimits.TryRemove(entry.Key, out _);
}
SweepStaleSessions();
}
});