public async Task <bool> OnHandle(MessageContext context, NotifyServerConnectionRequestDataMessage message)
        {
            var session = context.Session;

            session.Logger.Verbose("Handshake:NotifyServerConnectionRequestData");
            if (message.InternalNetVersion != Constants.NetVersion ||
                message.Version != _networkOptions.Version)
            {
                // ReSharper disable RedundantAnonymousTypePropertyName
                session.Logger.Warning(
                    "Protocol version mismatch - Client={@ClientVersion} Server={@ServerVersion}",
                    new { NetVersion = message.InternalNetVersion, Version = message.Version },
                    new { NetVersion = Constants.NetVersion, Version = _networkOptions.Version });
                // ReSharper restore RedundantAnonymousTypePropertyName

                session.Send(new NotifyProtocolVersionMismatchMessage());
                var _ = session.CloseAsync();
            }

            _sessionManager.AddSession(session.HostId, session);
            session.HandhsakeEvent.Set();
            session.State = SessionState.Connected;
            session.Send(new NotifyServerConnectSuccessMessage(
                             session.HostId, _networkOptions.Version, session.RemoteEndPoint)
                         );
            return(true);
        }
        public async Task <bool> OnHandle(MessageContext context, C2S_RequestCreateUdpSocketMessage message)
        {
            var session = context.Session;

            session.Logger.Debug("C2S_RequestCreateUdpSocketMessage {@Message}", message);
            if (!_udpSocketManager.IsRunning)
            {
                return(true);
            }

            _magicNumberSessionManager.RemoveSession(session.HolepunchMagicNumber);

            var socket = _udpSocketManager.NextSocket();

            session.UdpSocket            = socket;
            session.HolepunchMagicNumber = Guid.NewGuid();
            _magicNumberSessionManager.AddSession(session.HolepunchMagicNumber, session);
            session.Send(new S2C_RequestCreateUdpSocketMessage(
                             new IPEndPoint(_udpSocketManager.Address, ((IPEndPoint)socket.Channel.LocalAddress).Port))
                         );
            return(true);
        }
        private void ScheduleRetryUdpOrHolepunchIfRequired()
        {
            void RetryUdpOrHolepunchIfRequired(object context, object state)
            {
                var server = (ProudNetServerService)context;

                try
                {
                    var log              = server._logger;
                    var groupManager     = server._groupManager;
                    var udpSocketManager = server._udpSocketManager;
                    var configuration    = server._networkOptions;
                    if (!udpSocketManager.IsRunning || server.IsShuttingDown || !server.IsRunning)
                    {
                        return;
                    }

                    log.Verbose("RetryUdpOrHolepunchIfRequired");
                    foreach (var group in groupManager.Values)
                    {
                        var now = DateTimeOffset.Now;

                        // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
                        foreach (IP2PMemberInternal member in group)
                        {
                            if (!(member is ProudSession session))
                            {
                                continue;
                            }

                            // Retry udp relay
                            if (session.UdpSocket != null)
                            {
                                var diff = now - session.LastUdpPing;
                                if (!session.UdpEnabled)
                                {
                                    _magicNumberSessionManager.RemoveSession(session.HolepunchMagicNumber);

                                    session.Logger.Information("Trying to switch to udp relay");
                                    var socket = udpSocketManager.NextSocket();
                                    session.UdpSocket            = socket;
                                    session.HolepunchMagicNumber = Guid.NewGuid();
                                    _magicNumberSessionManager.AddSession(session.HolepunchMagicNumber, session);
                                    member.Send(new S2C_RequestCreateUdpSocketMessage(
                                                    new IPEndPoint(udpSocketManager.Address,
                                                                   ((IPEndPoint)socket.Channel.LocalAddress).Port)));
                                }
                                else if (diff >= configuration.PingTimeout)
                                {
                                    session.Logger.Information("Fallback to tcp relay by server");

                                    //member.Session.UdpEnabled = false;
                                    //server.SessionsByUdpId.Remove(member.Session.UdpSessionId);
                                    member.Send(new NotifyUdpToTcpFallbackByServerMessage());
                                }
                            }

                            // Skip p2p stuff when not enabled
                            if (!group.AllowDirectP2P)
                            {
                                continue;
                            }

                            // Retry p2p holepunch
                            foreach (var stateToTarget in member.ConnectionStates.Values)
                            {
                                var stateFromTarget = stateToTarget.RemotePeer.ConnectionStates.GetValueOrDefault(member.HostId);
                                if (stateFromTarget == null)
                                {
                                    continue;
                                }

                                if (!stateToTarget.IsJoined || !stateFromTarget.IsJoined)
                                {
                                    continue;
                                }

                                if (!(stateToTarget.RemotePeer is ProudSession targetSession) || !targetSession.UdpEnabled ||
                                    !session.UdpEnabled)
                                {
                                    continue;
                                }

                                if (stateToTarget.IsInitialized)
                                {
                                    var diff = now - stateToTarget.LastHolepunch;
                                    if (!stateToTarget.HolepunchSuccess &&
                                        diff >= configuration.HolepunchTimeout &&
                                        stateToTarget.RetryCount < configuration.HolepunchMaxRetryCount)
                                    {
                                        session.Logger
                                        .ForContext("P2PState", new
                                        {
                                            Target = stateToTarget.RemotePeer.HostId,
                                            stateToTarget.IsInitialized,
                                            stateToTarget.IsJoined,
                                            stateToTarget.JitTriggered,
                                            stateToTarget.HolepunchSuccess,
                                            stateToTarget.LastHolepunch
                                        })
                                        .Information("Trying to reconnect P2P to {TargetHostId}",
                                                     stateToTarget.RemotePeer.HostId);

                                        targetSession.Logger
                                        .ForContext("P2PState", new
                                        {
                                            Target = stateFromTarget.RemotePeer.HostId,
                                            stateFromTarget.IsInitialized,
                                            stateFromTarget.IsJoined,
                                            stateFromTarget.JitTriggered,
                                            stateFromTarget.HolepunchSuccess,
                                            stateFromTarget.LastHolepunch
                                        })
                                        .Information("Trying to reconnect P2P to {TargetHostId}",
                                                     member.HostId);

                                        stateToTarget.RetryCount++;
                                        stateFromTarget.RetryCount++;
                                        stateToTarget.JitTriggered                  = stateFromTarget.JitTriggered = false;
                                        stateToTarget.PeerUdpHolepunchSuccess       =
                                            stateFromTarget.PeerUdpHolepunchSuccess = false;
                                        stateToTarget.LastHolepunch                 = stateFromTarget.LastHolepunch = now;
                                        member.Send(new RenewP2PConnectionStateMessage(stateToTarget.RemotePeer.HostId));
                                        stateToTarget.RemotePeer.Send(new RenewP2PConnectionStateMessage(member.HostId));
                                    }
                                }
                                else
                                {
                                    session.Logger.Debug("Initialize P2P with {TargetHostId}",
                                                         stateToTarget.RemotePeer.HostId);
                                    targetSession.Logger.Debug("Initialize P2P with {TargetHostId}", member.HostId);
                                    stateToTarget.LastHolepunch = stateFromTarget.LastHolepunch = DateTimeOffset.Now;
                                    stateToTarget.IsInitialized = stateFromTarget.IsInitialized = true;
                                    member.Send(new P2PRecycleCompleteMessage(stateToTarget.RemotePeer.HostId));
                                    stateToTarget.RemotePeer.Send(new P2PRecycleCompleteMessage(member.HostId));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "RetryUdpOrHolepunchIfRequired exception");
                }
                finally
                {
                    server.ScheduleRetryUdpOrHolepunchIfRequired();
                }
            }

            if (!IsShuttingDown && IsRunning)
            {
                _schedulerService.ScheduleAsync(RetryUdpOrHolepunchIfRequired, this, null, TimeSpan.FromSeconds(5));
            }
        }
Example #4
0
        public override void ChannelRead(IChannelHandlerContext context, object obj)
        {
            var message = obj as UdpMessage;

            Debug.Assert(message != null);

            try
            {
                var session = _udpSessionManager.GetSession(message.SessionId);
                if (session == null)
                {
                    if (message.Content.GetByte(0) != (byte)ProudCoreOpCode.ServerHolepunch)
                    {
                        _logger.Warning("<{EndPoint}> Expected ServerHolepunch as first udp message but got {MessageType}",
                                        message.EndPoint.ToString(), (ProudCoreOpCode)message.Content.GetByte(0));
                        return;
                    }

                    var holepunch = (ServerHolepunchMessage)CoreMessageDecoder.Decode(_serializer, message.Content);
                    session = _magicNumberSessionManager.GetSession(holepunch.MagicNumber);
                    if (session == null)
                    {
                        _logger.Warning("<{EndPoint}> Invalid holepunch magic number={MagicNumber}",
                                        message.EndPoint.ToString(), holepunch.MagicNumber);
                        return;
                    }

                    if (session.UdpSocket != Socket)
                    {
                        _logger.Warning("<{EndPoint}> Client is sending to the wrong udp socket",
                                        message.EndPoint.ToString());
                        return;
                    }

                    session.UdpSessionId = message.SessionId;
                    session.UdpEndPoint  = message.EndPoint;
                    _udpSessionManager.AddSession(session.UdpSessionId, session);
                    session.UdpSocket.Send(
                        new ServerHolepunchAckMessage(session.HolepunchMagicNumber, session.UdpEndPoint),
                        session.UdpEndPoint
                        );
                    return;
                }

                if (session.UdpSocket != Socket)
                {
                    _logger.Warning("<{EndPoint}> Client is sending to the wrong udp socket",
                                    message.EndPoint.ToString());
                    return;
                }

                session.UdpEndPoint = message.EndPoint;
                var recvContext = new MessageContext
                {
                    Session     = session,
                    Message     = message.Content.Retain(),
                    UdpEndPoint = message.EndPoint
                };

                session?.Channel?.Pipeline?.Context <MessageContextDecoder>()?.FireChannelRead(recvContext);
            }
            finally
            {
                message.Content.Release();
            }
        }