예제 #1
0
        private void AckHandler(IncomingCommand command)
        {
            uint currentRoundTripTime = command.TimestampOfReceive - command.AckReceivedSentTime;

            // Find a channel that the command came, and remove a sent command.
            UdpChannel channel = _channels[command.Channel];
            OutgoingCommand reliableCommand = channel.ReliableSendQueue.ReceiveAck(command);

            //if reliableCommand is null, current command is already received.
            if (reliableCommand == null)
                return;

            if (reliableCommand.CommandType == CommandType.SNTP)
            {
                ProcessSntp(command, currentRoundTripTime);
            }

            _roundTripTime.Update((int)currentRoundTripTime);

            if (_allowStatistics)
            {
                _statistics.UpdateRoundTripTime(_roundTripTime);
                _statistics.ReceiveAck(command.TimestampOfReceive);
            }
        }
        /// <summary>
        /// Process a received command.
        /// (A ack command is processed by a receive thread, other commands is dispatched in a rendering thread.)
        /// </summary>
        private void ExecuteReceiveCommand(IncomingCommand command)
        {
            if (_udpSocket.State != SocketState.Connected)
            {
                return;
            }

            switch (command.Type)
            {
            case CommandType.Acknowledge:
                // Already process a acknowledge command in a receive thread.
                return;

            case CommandType.Disconnect:
                byte[] payload = command.GetPayload();
                int    offset  = 0;

                DisconnectReason disconnectType = (DisconnectReason)ByteRead.GetInt(payload, ref offset);

                var detailMessage = ByteRead.GetString <int>(payload, ref offset, Encoding.UTF8);
                Log(LogLevel.Error, "Disconnect this client[{0}] : {1}", disconnectType, detailMessage);

                Disconnect(disconnectType, false);

                return;

            case CommandType.Reliable:
            case CommandType.Unreliable:
                EnqueueIncomingCommand(command);
                return;

            case CommandType.Fragmented:
                if (command.FragmentNumber > command.FragmentCount ||
                    command.FragmentOffset >= command.TotalLength ||
                    command.FragmentOffset + command.GetPayload().Length > command.TotalLength)
                {
                    Log(LogLevel.Error, "Received fragment has bad size: {0}", command);
                    return;
                }

                if (EnqueueIncomingCommand(command))
                {
                    UdpChannel udpChannel = _channels[command.Channel];

                    ReliableReceiveQueue reliableReceiveQueue = udpChannel.ReliableReceiveQueue as ReliableReceiveQueue;
                    if (reliableReceiveQueue != null)
                    {
                        reliableReceiveQueue.ReceiveFragmentCommand(command);
                    }
                }
                return;

            default:
                Log(LogLevel.Error, "Unknown command received {0}", command.Type);
                return;
            }
        }
        /// <summary>
        /// Adds a command to the end of the incoming queue.
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        private bool EnqueueIncomingCommand(IncomingCommand command)
        {
            UdpChannel channel;

            if (!_channels.TryGetValue(command.Channel, out channel))
            {
                Log(LogLevel.Error, "Received a command for non-existing channel: {0}", command.Channel);
                return(false);
            }

            ReceiveQueueBase receiveQueue = command.IsReliable ? channel.ReliableReceiveQueue : channel.UnreliableReceiveQueue;

            return(receiveQueue.EnqueueIncomingCommand(command));
        }
        public static OutgoingCommand CreateAck(this IncomingCommand command)
        {
            if (false == command.IsReliable)
            {
                return(null);
            }

            var payload = ByteBufferFactory.NewBuffer();

            payload.WriteLong(command.ReliableSequenceNumber);
            payload.WriteLong(command.ServerSentTime);

            return(new OutgoingCommand(CommandType.Acknowledge, command.Channel, payload.ToArray()));
        }
예제 #5
0
        private void SendAckFromCommand(IncomingCommand command)
        {
            bool sendable;

            UdpChannel channel = _channels[command.Channel];
            lock (channel.AckQueue)
            {
                sendable = channel.AckQueue.EnqueueOutgoingCommand(command.CreateAck());
            }

            if (sendable)
            {
                SendAck();
            }
        }
        /// <summary>
        /// Read incoming commands. This method must be called in a main thread.
        /// If an application was developed by Unity3D, this method must be called in a script thread.
        /// </summary>
        public IEnumerable <IByteBuffer> ReadMessages()
        {
            if (_udpSocket.State != SocketState.Connected)
            {
                yield return(null);
            }

            IncomingCommand        command  = null;
            List <IncomingCommand> commands = new List <IncomingCommand>();

            for (int i = 0; i < _channels.Count; i++)
            {
                // To prevent to receive a new command in a receive thread when read commands.
                // If a receive thread receive a new command when read commands, need to ignore a new command that receive when read commands.
                // If don't ignore a new command when read commands, continue processing a newer command.
                int unreliableCommandCount = _channels[i].UnreliableReceiveQueue.Count;
                int reliableCommandCount   = _channels[i].ReliableReceiveQueue.Count;

                for (int j = 0; j < unreliableCommandCount; j++)
                {
                    if (_channels[i].UnreliableReceiveQueue.TryProcessCommand(out command))
                    {
                        commands.Add(command);
                    }
                }

                for (int j = 0; j < reliableCommandCount; j++)
                {
                    if (_channels[i].ReliableReceiveQueue.TryProcessCommand(out command))
                    {
                        commands.Add(command);
                    }
                }
            }

            foreach (var item in commands)
            {
                IByteBuffer payload = FetchPayload(item);
                if (payload == null)
                {
                    Disconnect(DisconnectReason.InvalidConnection, true);
                }
                yield return(payload);
            }
        }
예제 #7
0
        private void ProcessSntp(IncomingCommand command, uint currentRoundTripTime)
        {
            uint currentServerTime = 0;

            if (_roundTripTime.MeanOfRoundTripTime > currentRoundTripTime)
            {
                currentServerTime = (command.ServerSentTime + (currentRoundTripTime >> 1));
            }
            else
            {
                currentServerTime = (command.ServerSentTime + ((uint)_roundTripTime.MeanOfRoundTripTime >> 1));
            }

            Interlocked.Exchange(ref _serverTimeOffset, (int)(currentServerTime - command.TimestampOfReceive));

            if (_listener != null)
            {
                _listener.OnStatusChanged(StatusCode.ServerTimeAvailable, "Available server time");
            }
        }
예제 #8
0
        /// <summary>
        /// Fetch a payload. If a incoming command is invalid, return null.
        /// </summary>
        /// <returns>A payload in a incoming command.</returns>
        private IByteBuffer FetchPayload(IncomingCommand command)
        {
            if (false == command.IsPayloadAvailable())
            {
                return(null);
            }

            // The length of InitialResponse is 2. Thus payload length should be bigger then 2.
            if (command.GetPayload().Length < 2)
            {
                Log(LogLevel.Error, "Incoming UDP data is too short; Length [{0}] ", command.GetPayload().Length);
                return(null);
            }

            if (command.IsEncrypted)
            {
                try
                {
                    command.SetPayload(_cipher.Decrypt(command.GetPayload()));
                }
                catch (Exception)
                {
                    Log(LogLevel.Error, "Invalid encrypted data");
                    return(null);
                }
            }

            var payload = ByteBufferFactory.NewBuffer(command.GetPayload());

            byte serializerVersion = payload.ReadByte();

            if (serializerVersion != DataSerializer.Version)
            {
                Log(LogLevel.Error, "Unknown serializer Version");
                return(null);
            }

            return(payload);
        }
예제 #9
0
        /// <summary>
        /// Called by a receive thread.
        /// </summary>
        void INetworkBroker.OnReceive(IByteBuffer buffer)
        {
            uint currentTime = EnvironmentTimer.GetTickCount();
            int  bufferCount = buffer.Count;

            CommandType ct = (CommandType)buffer.ReadByte();

            if (ct != CommandType.None)
            {
                return;
            }

            //peerID (4 bytes)
            int peerID = buffer.ReadInt();

            if (_peerId > 0 && peerID != _peerId)
            {
                Log(LogLevel.Error, "A assigned peerID [{0}] is different from recevied peerID [{1}] from server", peerID, _peerId);
                Disconnect(DisconnectReason.InvalidConnection, false);
            }

            //timestamp of sent time (8 byte)
            uint serverSentTime = (uint)buffer.ReadLong();

            //the number of commands in a incoming buffer (2 byte)
            short commandCount = buffer.ReadShort();

            //crc value (8 byte)
            long crc = buffer.ReadLong();

            if (IsCrcEnabled)
            {
                int writeIndex = buffer.WriteIndex;

                buffer.WriteIndex = buffer.ReadIndex - (int)Lengths.Crc;
                buffer.WriteLong(0);
                buffer.WriteIndex = writeIndex;

                long calc = buffer.CalculateCrc();

                if (crc != calc)
                {
                    if (_allowStatistics)
                    {
                        _statistics.ErrorCrc();
                    }

                    return;
                }
            }

            for (int i = 0; i < commandCount; i++)
            {
                IncomingCommand command = new IncomingCommand(buffer, currentTime, serverSentTime);

                if (command.IsReliable)
                {
                    SendAckFromCommand(command);
                }

                if (command.Type == CommandType.Acknowledge)
                {
                    AckHandler(command);
                }
                else
                {
                    EnqueueRenderingQueue(() => ExecuteReceiveCommand(command));
                }
            }

            if (_allowStatistics)
            {
                _statistics.ReceiveBytes(bufferCount, currentTime);
                _statistics.ReceiveMtu(serverSentTime);
                _statistics.ReceiveIncomingCommand(commandCount);
            }
        }