using System.Net; using System.Net.Sockets; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RPG.Services.Core.Session; using RPG.Services.Gateserver.Options; using RPG.Services.Gateserver.Session; namespace RPG.Services.Gateserver.Network.Tcp; internal class TcpGateway { private readonly IOptions _options; private readonly SessionManager _sessionManager; private readonly ILogger _logger; private Socket? _socket; private CancellationTokenSource? _acceptCancellation; private Task? _acceptTask; private ulong _sessionIdCounter; public TcpGateway(IOptions options, SessionManager sessionManager, ILogger logger) { _options = options; _sessionManager = sessionManager; _logger = logger; } public void Start() { IPEndPoint bindEndPoint = _options.Value.BindEndPoint; _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Bind(bindEndPoint); _socket.Listen(100); _acceptCancellation = new(); _acceptTask = RunAcceptLoopAsync(_acceptCancellation.Token); _logger.LogInformation("Listening at tcp://{endPoint}", bindEndPoint); } public async Task StopAsync() { if (_acceptCancellation != null && _acceptTask != null) { await _acceptCancellation.CancelAsync(); await _acceptTask; } _socket?.Close(); } private async Task RunAcceptLoopAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { Socket clientSocket = await _socket!.AcceptAsync(cancellationToken); _logger.LogInformation("New TCP connection from {remoteEndPoint}", clientSocket.RemoteEndPoint); _ = RunSessionAsync(clientSocket); } } catch (Exception exception) when (exception is not OperationCanceledException) { throw; } catch { /* Operation canceled */ } } private async Task RunSessionAsync(Socket socket) { NetworkSession? session = _sessionManager.Create(Interlocked.Increment(ref _sessionIdCounter)); if (session == null) return; try { session.Socket = socket; await session.RunAsync(); } catch (Exception exception) when (exception is not OperationCanceledException) { _logger.LogError("Unhandled exception occurred: {exception}", exception); } catch { /* Operation canceled */ } finally { _sessionManager.Remove(session); } } }