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())); }
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); } }
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"); } }
/// <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); }
/// <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); } }