Server closes connections gracefully so kick/ban/reset/shutdown messages arrive; status dot and HUD yellow until really connected (also while reconnecting)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -89,6 +89,9 @@ public sealed class Connection
|
||||
|
||||
readonly TcpClient client;
|
||||
readonly NetworkStream stream;
|
||||
bool closing; // Meldung gesendet, warten bis der Spieler sie gelesen hat (siehe CloseAfterSend)
|
||||
DateTime closeDeadline;
|
||||
string closingReason;
|
||||
readonly byte[] readBuffer = new byte[64 * 1024];
|
||||
byte[] buffer = new byte[64 * 1024];
|
||||
int count;
|
||||
@@ -106,6 +109,11 @@ public sealed class Connection
|
||||
{
|
||||
var messages = new List<Message>();
|
||||
if (IsClosed) return messages;
|
||||
if (closing)
|
||||
{
|
||||
DrainWhileClosing();
|
||||
return messages;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (client.Client.Poll(0, SelectMode.SelectRead) && client.Available == 0)
|
||||
@@ -131,7 +139,7 @@ public sealed class Connection
|
||||
|
||||
public void Send(MsgType type, byte[] payload = null)
|
||||
{
|
||||
if (IsClosed) return;
|
||||
if (IsClosed || closing) return;
|
||||
try
|
||||
{
|
||||
byte[] frame = Wire.Frame(type, payload);
|
||||
@@ -143,6 +151,38 @@ public sealed class Connection
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sauber trennen, nachdem eine letzte Meldung gesendet wurde (Kick/Ban-Grund, Neu-Verbinden, Shutdown).
|
||||
/// Sofortiges Close() mit noch ungelesenen Daten des Spielers (er schickt ständig Positionen) führt zu einem
|
||||
/// TCP-Reset - dann geht die gerade gesendete Meldung verloren. Deshalb: nur die Sende-Richtung schließen,
|
||||
/// eingehende Daten noch kurz lesen und verwerfen, dann erst schließen.
|
||||
/// </summary>
|
||||
public void CloseAfterSend(string reason)
|
||||
{
|
||||
if (IsClosed || closing) return;
|
||||
closing = true;
|
||||
closingReason = reason;
|
||||
closeDeadline = DateTime.UtcNow.AddSeconds(3);
|
||||
try { client.Client.Shutdown(SocketShutdown.Send); } catch { Close(reason); }
|
||||
}
|
||||
|
||||
public bool IsClosing => closing && !IsClosed;
|
||||
|
||||
void DrainWhileClosing()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (stream.DataAvailable)
|
||||
if (stream.Read(readBuffer, 0, readBuffer.Length) <= 0) break;
|
||||
bool remoteClosed = client.Client.Poll(0, SelectMode.SelectRead) && client.Available == 0;
|
||||
if (remoteClosed || DateTime.UtcNow >= closeDeadline) Close(closingReason);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Close(closingReason);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close(string reason)
|
||||
{
|
||||
if (IsClosed) return;
|
||||
|
||||
Reference in New Issue
Block a user