public async Task StartAsync(CancellationToken cancellationToken) { if (IsRunning) { throw new InvalidOperationException("Server is already running"); } var tcpListener = _networkOptions.TcpListener; var udpAddress = _networkOptions.UdpAddress; var udpListenerPorts = _networkOptions.UdpListenerPorts; _logger.Information("Starting - tcp={TcpEndPoint} udp={UdpAddress} udp-port={UdpPorts}", tcpListener, udpAddress, udpListenerPorts); try { _listenerThreads = _threadingOptions.SocketListenerThreadsFactory(); _workerThreads = _threadingOptions.SocketWorkerThreadsFactory(); await new ServerBootstrap() .Group(_listenerThreads, _workerThreads) .Channel <TcpServerSocketChannel>() .Handler(new ActionChannelInitializer <IServerSocketChannel>(ch => { })) .ChildHandler(new ActionChannelInitializer <ISocketChannel>(ch => { var coreMessageHandler = new MessageHandler(_serviceProvider, new DefaultMessageHandlerResolver( new[] { typeof(AuthenticationHandler).Assembly }, typeof(ICoreMessage) ), _schedulerService ); var internalRmiMessageHandler = new MessageHandler( _serviceProvider, new DefaultMessageHandlerResolver( new[] { typeof(ReliablePingMessage).Assembly }, typeof(IMessage) ), _schedulerService ); var rmiMessageHandler = new MessageHandler( _serviceProvider, _serviceProvider.GetRequiredService <IMessageHandlerResolver>(), _schedulerService ); coreMessageHandler.UnhandledMessage += ctx => ctx.Session.Logger.Debug("Unhandled core message={@Message}", ctx.Message); internalRmiMessageHandler.UnhandledMessage += ctx => ctx.Session.Logger.Debug("Unhandled internal rmi message={@Message}", ctx.Message); rmiMessageHandler.UnhandledMessage += OnUnhandledRmi; ch.Pipeline .AddLast(_serviceProvider.GetRequiredService <SessionHandler>()) .AddLast(_serviceProvider.GetRequiredService <ProudFrameDecoder>()) .AddLast(_serviceProvider.GetRequiredService <ProudFrameEncoder>()) .AddLast(_serviceProvider.GetRequiredService <MessageContextDecoder>()) .AddLast(_serviceProvider.GetRequiredService <CoreMessageDecoder>()) .AddLast(_serviceProvider.GetRequiredService <CoreMessageEncoder>()) .AddLast(new FlowControlHandler(false)) .AddLast(Constants.Pipeline.CoreMessageHandlerName, coreMessageHandler) .AddLast(_serviceProvider.GetRequiredService <SendContextEncoder>()) .AddLast(_serviceProvider.GetRequiredService <MessageDecoder>()) .AddLast(_serviceProvider.GetRequiredService <MessageEncoder>()) // MessageHandler discards the message after handling // so internal messages wont reach the rmiMessageHandler .AddLast(Constants.Pipeline.InternalMessageHandlerName, internalRmiMessageHandler) .AddLast(_serviceProvider.GetRequiredService <MessageDecoder>()) .AddLast(rmiMessageHandler) .AddLast(_serviceProvider.GetRequiredService <ErrorHandler>()); })) .ChildOption(ChannelOption.TcpNodelay, !_networkOptions.EnableNagleAlgorithm) .ChildOption(ChannelOption.AutoRead, false) .ChildAttribute(ChannelAttributes.ServiceProvider, _serviceProvider) .ChildAttribute(ChannelAttributes.Session, default(ProudSession)) .BindAsync(_networkOptions.TcpListener); if (udpListenerPorts != null) { _udpSocketManager.Listen(udpAddress, tcpListener.Address, udpListenerPorts, _workerThreads); } } catch (Exception ex) { _logger.Error(ex, "Unable to start server - tcp={TcpEndPoint} udp={UdpAddress} udp-port={UdpPorts}", tcpListener, udpAddress, udpListenerPorts); await ShutdownThreads(); ex.Rethrow(); } IsRunning = true; OnStarted(); ScheduleRetryUdpOrHolepunchIfRequired(); }