Performance: frame broadcasts once, Socket.Select instead of fixed sleep, reuse message list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+20
-2
@@ -154,7 +154,7 @@ public sealed class GameServer
|
|||||||
PollClients();
|
PollClients();
|
||||||
RunCommands();
|
RunCommands();
|
||||||
Timers();
|
Timers();
|
||||||
Thread.Sleep(2);
|
WaitForTraffic();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var c in clients.Values)
|
foreach (var c in clients.Values)
|
||||||
@@ -672,8 +672,26 @@ public sealed class GameServer
|
|||||||
|
|
||||||
void Broadcast(MsgType type, byte[] payload, int except = -1)
|
void Broadcast(MsgType type, byte[] payload, int except = -1)
|
||||||
{
|
{
|
||||||
|
byte[] frame = null; // nur einmal rahmen, nicht pro Spieler
|
||||||
foreach (var c in clients.Values)
|
foreach (var c in clients.Values)
|
||||||
if (c.Id != except && c.State == ClientState.Joined) c.Conn.Send(type, payload);
|
if (c.Id != except && c.State == ClientState.Joined) c.Conn.SendFrame(frame ??= Wire.Frame(type, payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly List<Socket> waitList = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bis zu ein paar Millisekunden warten, aber sofort weitermachen, sobald ein Spieler etwas schickt oder jemand
|
||||||
|
/// verbinden will - schneller als festes Schlafen und ohne Leerlauf-Last.
|
||||||
|
/// </summary>
|
||||||
|
void WaitForTraffic()
|
||||||
|
{
|
||||||
|
waitList.Clear();
|
||||||
|
waitList.Add(listener.Server);
|
||||||
|
foreach (var c in clients.Values)
|
||||||
|
if (!c.Conn.IsClosed) waitList.Add(c.Conn.Socket);
|
||||||
|
int micros = clients.Count == 0 ? 50_000 : 5_000; // leerer Server: seltener aufwachen
|
||||||
|
try { Socket.Select(waitList, null, null, micros); }
|
||||||
|
catch { Thread.Sleep(2); } // z.B. Socket wurde gerade geschlossen
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- Konsole
|
// ---------------------------------------------------------------- Konsole
|
||||||
|
|||||||
+17
-6
@@ -101,6 +101,7 @@ public sealed class Connection
|
|||||||
DateTime closeDeadline;
|
DateTime closeDeadline;
|
||||||
string closingReason;
|
string closingReason;
|
||||||
readonly byte[] readBuffer = new byte[64 * 1024];
|
readonly byte[] readBuffer = new byte[64 * 1024];
|
||||||
|
readonly List<Message> received = new();
|
||||||
byte[] buffer = new byte[64 * 1024];
|
byte[] buffer = new byte[64 * 1024];
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
@@ -113,9 +114,16 @@ public sealed class Connection
|
|||||||
RemoteAddress = client.Client.RemoteEndPoint?.ToString() ?? "?";
|
RemoteAddress = client.Client.RemoteEndPoint?.ToString() ?? "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Socket zum Warten auf Daten (Socket.Select im Server-Loop).</summary>
|
||||||
|
public Socket Socket => client.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Neue Nachrichten. Die Liste wird beim nächsten Aufruf wiederverwendet (keine Allokation pro Durchlauf).
|
||||||
|
/// </summary>
|
||||||
public List<Message> Poll()
|
public List<Message> Poll()
|
||||||
{
|
{
|
||||||
var messages = new List<Message>();
|
var messages = received;
|
||||||
|
messages.Clear();
|
||||||
if (IsClosed) return messages;
|
if (IsClosed) return messages;
|
||||||
if (closing)
|
if (closing)
|
||||||
{
|
{
|
||||||
@@ -124,12 +132,13 @@ public sealed class Connection
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (client.Client.Poll(0, SelectMode.SelectRead) && client.Available == 0)
|
if (client.Available == 0)
|
||||||
{
|
{
|
||||||
Close("Connection closed");
|
// Getrennt? "lesbar, aber keine Daten" = Gegenseite hat geschlossen
|
||||||
|
if (client.Client.Poll(0, SelectMode.SelectRead) && client.Available == 0) Close("Connection closed");
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
while (stream.DataAvailable)
|
while (client.Available > 0)
|
||||||
{
|
{
|
||||||
int read = stream.Read(readBuffer, 0, readBuffer.Length);
|
int read = stream.Read(readBuffer, 0, readBuffer.Length);
|
||||||
if (read <= 0) break;
|
if (read <= 0) break;
|
||||||
@@ -145,12 +154,14 @@ public sealed class Connection
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(MsgType type, byte[] payload = null)
|
public void Send(MsgType type, byte[] payload = null) => SendFrame(Wire.Frame(type, payload));
|
||||||
|
|
||||||
|
/// <summary>Fertig gerahmte Nachricht senden (Broadcast: einmal rahmen, an alle schicken).</summary>
|
||||||
|
public void SendFrame(byte[] frame)
|
||||||
{
|
{
|
||||||
if (IsClosed || closing) return;
|
if (IsClosed || closing) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] frame = Wire.Frame(type, payload);
|
|
||||||
stream.Write(frame, 0, frame.Length);
|
stream.Write(frame, 0, frame.Length);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
Reference in New Issue
Block a user