95 lines
2.7 KiB
C#
95 lines
2.7 KiB
C#
|
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<GatewayOptions> _options;
|
|||
|
private readonly SessionManager _sessionManager;
|
|||
|
private readonly ILogger _logger;
|
|||
|
|
|||
|
private Socket? _socket;
|
|||
|
|
|||
|
private CancellationTokenSource? _acceptCancellation;
|
|||
|
private Task? _acceptTask;
|
|||
|
|
|||
|
private ulong _sessionIdCounter;
|
|||
|
|
|||
|
public TcpGateway(IOptions<GatewayOptions> options, SessionManager sessionManager, ILogger<TcpGateway> 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<NetworkSession>(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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|