namespace Supercell.Laser.Server.Networking { using Supercell.Laser.Server.Networking.UDP.Game; using Supercell.Laser.Titan.DataStream; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; public static class UDPGateway { private static Socket Socket; private static long SessionCounter; // ConcurrentDictionary: CreateSocket() (TCP-потоки) и ReceiveCallback (UDP-потоки) // раньше писали/читали общий Dictionary без лока — гонка могла испортить структуру. private static ConcurrentDictionary Sockets; // Несколько параллельных приёмов, чтобы под нагрузкой не терять боевые пакеты // из-за однопоточного BeginReceiveFrom → WaitOne цикла. private static int ReceiverCount; public static void Init(string host, int port) { Sockets = new ConcurrentDictionary(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); Socket.Bind(new IPEndPoint(IPAddress.Parse(host), port)); ReceiverCount = Math.Max(Environment.ProcessorCount, 4); for (int i = 0; i < ReceiverCount; i++) BeginReceive(); Logger.Print($"UDP Server started at {host}:{port} ({ReceiverCount} parallel receivers)"); } private static void BeginReceive() { try { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] buffer = new byte[1500]; State state = new State { RemoteEndPoint = remoteEndPoint, Buffer = buffer }; Socket.BeginReceiveFrom(buffer, 0, 1500, SocketFlags.None, ref remoteEndPoint, new AsyncCallback(ReceiveCallback), state); } catch (Exception e) { Logger.Error($"UDP BeginReceive error: {e.GetType().Name} - {e.Message}"); } } private static void ReceiveCallback(IAsyncResult result) { State state = (State)result.AsyncState; byte[] buffer = state.Buffer; EndPoint remoteEndPoint = state.RemoteEndPoint; int r = 0; try { r = Socket.EndReceiveFrom(result, ref remoteEndPoint); } catch (Exception e) { Logger.Error($"UDP EXCEPTION: {e.GetType().Name} - {e.Message}"); } finally { // Немедленно перевывешиваем следующий приём, чтобы всегда висело // ReceiverCount незавершённых BeginReceiveFrom. BeginReceive(); } if (r <= 0) return; try { WireDump.Write("UDP-RAW-RECV", buffer, 0, r); ByteStream stream = new ByteStream(buffer, r); long sessionId = stream.ReadLong(); stream.ReadShort(); if (!Sockets.TryGetValue(sessionId, out UDPSocket client)) return; if (!client.IsConnected) { client.SetEndPoint(remoteEndPoint); if (client.Battle == null) return; if (!client.IsSpectator) client.Battle.GetPlayerBySessionId(client.SessionId).GameListener = new UDPGameListener(client, client.TCPConnection); } client.ProcessReceive(stream); } catch (Exception e) { Logger.Error($"EXCEPTION: {e.GetType().Name} - {e.Message}\n{e.StackTrace}"); } } private class State { public byte[] Buffer; public EndPoint RemoteEndPoint; } public static UDPSocket CreateSocket() { long id = System.Threading.Interlocked.Increment(ref SessionCounter); UDPSocket socket = new UDPSocket(id); Sockets[id] = socket; return socket; } public static void RemoveSocket(long sessionId) { Sockets.TryRemove(sessionId, out _); } public static void SendTo(byte[] data, int startIndex, int count, EndPoint remoteEndPoint) { try { if (remoteEndPoint == null) return; Socket.SendTo(data, startIndex, count, SocketFlags.None, remoteEndPoint); } catch (SocketException) { } } } }