private void ProcessMtuPacket(NetPacket packet) { if (packet.Size == 1 || packet.RawData[1] >= NetConstants.PossibleMtu.Length) { return; } //MTU auto increase if (packet.Property == PacketProperty.MtuCheck) { if (packet.Size != NetConstants.PossibleMtu[packet.RawData[1]]) { return; } _mtuCheckAttempts = 0; NetUtils.DebugWrite("MTU check. Resend: " + packet.RawData[1]); var mtuOkPacket = _packetPool.Get(PacketProperty.MtuOk, 1); mtuOkPacket.RawData[1] = packet.RawData[1]; SendPacket(mtuOkPacket); } else if (packet.RawData[1] > _mtuIdx) //MtuOk { lock (_mtuMutex) { _mtuIdx = packet.RawData[1]; _mtu = NetConstants.PossibleMtu[_mtuIdx]; } //if maxed - finish. if (_mtuIdx == NetConstants.PossibleMtu.Length - 1) { _finishMtu = true; } NetUtils.DebugWrite("MTU ok. Increase to: " + _mtu); } }
internal NetPeer(NetManager peerListener, NetEndPoint remoteEndPoint, long connectId) { Statistics = new NetStatistics(); _packetPool = peerListener.PacketPool; _peerListener = peerListener; _remoteEndPoint = remoteEndPoint; _avgRtt = 0; _rtt = 0; _pingSendTimer = 0; _reliableOrderedChannel = new ReliableChannel(this, true); _reliableUnorderedChannel = new ReliableChannel(this, false); _sequencedChannel = new SequencedChannel(this); _simpleChannel = new SimpleChannel(this); _holdedFragments = new Dictionary <ushort, IncomingFragments>(); _mergeData = _packetPool.Get(PacketProperty.Merged, NetConstants.MaxPacketSize); //if ID != 0 then we already connected _connectAttempts = 0; if (connectId == 0) { _connectId = DateTime.UtcNow.Ticks; SendConnectRequest(); } else { _connectId = connectId; _connectionState = ConnectionState.Connected; SendConnectAccept(); } NetUtils.DebugWrite(ConsoleColor.Cyan, "[CC] ConnectId: {0}", _connectId); }
//ProcessAck in packet public void ProcessAck(NetPacket packet) { int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize; if (packet.Size != validPacketSize) { NetUtils.DebugWrite("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; if (ackWindowStart > NetConstants.MaxSequence) { NetUtils.DebugWrite("[PA]Bad window start"); return; } //check relevance if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize) { NetUtils.DebugWrite("[PA]Old acks"); return; } byte[] acksData = packet.RawData; NetUtils.DebugWrite("[PA]AcksStart: {0}", ackWindowStart); int startByte = NetConstants.SequencedHeaderSize; Monitor.Enter(_pendingPackets); for (int i = 0; i < _windowSize; i++) { int ackSequence = (ackWindowStart + i) % NetConstants.MaxSequence; if (NetUtils.RelativeSequenceNumber(ackSequence, _localWindowStart) < 0) { //NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP OLD: " + ackSequence); //Skip old ack continue; } int currentByte = startByte + i / BitsInByte; int currentBit = i % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if STATS_ENABLED || DEBUG if (_pendingPackets[ackSequence % _windowSize].TimeStamp.HasValue) { _peer.Statistics.PacketLoss++; } #endif //NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP FALSE: " + ackSequence); //Skip false ack continue; } if (ackSequence == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } PendingPacket pendingPacket = _pendingPackets[ackSequence % _windowSize]; if (pendingPacket.Packet != null) { if (pendingPacket == _headPendingPacket) { _headPendingPacket = _headPendingPacket.Prev; } _peer.Recycle(pendingPacket.Packet); pendingPacket.Clear(); NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", ackSequence); } else { NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - false", ackSequence); } } Monitor.Exit(_pendingPackets); }
//Process incoming packet public void ProcessPacket(NetPacket packet) { if (packet.Sequence >= NetConstants.MaxSequence) { NetUtils.DebugWrite("[RR]Bad sequence"); return; } int relate = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); if (relateSeq > _windowSize) { NetUtils.DebugWrite("[RR]Bad sequence"); return; } //Drop bad packets if (relate < 0) { //Too old packet doesn't ack NetUtils.DebugWrite("[RR]ReliableInOrder too old"); return; } if (relate >= _windowSize * 2) { //Some very new packet NetUtils.DebugWrite("[RR]ReliableInOrder too new"); return; } //If very new - move window Monitor.Enter(_outgoingAcks); if (relate >= _windowSize) { //New window position int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; //Clean old data while (_remoteWindowStart != newWindowStart) { _outgoingAcks[_remoteWindowStart % _windowSize] = false; _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; } } //Final stage - process valid packet //trigger acks send _mustSendAcks = true; if (_outgoingAcks[packet.Sequence % _windowSize]) { NetUtils.DebugWrite("[RR]ReliableInOrder duplicate"); Monitor.Exit(_outgoingAcks); return; } //save ack _outgoingAcks[packet.Sequence % _windowSize] = true; Monitor.Exit(_outgoingAcks); //detailed check if (packet.Sequence == _remoteSequence) { NetUtils.DebugWrite("[RR]ReliableInOrder packet succes"); _peer.AddIncomingPacket(packet); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; if (_ordered) { NetPacket p; while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { //process holded packet _receivedPackets[_remoteSequence % _windowSize] = null; _peer.AddIncomingPacket(p); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { //process early packet _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } return; } //holded packet if (_ordered) { _receivedPackets[packet.Sequence % _windowSize] = packet; } else { _earlyReceived[packet.Sequence % _windowSize] = true; _peer.AddIncomingPacket(packet); } }
private void DataReceived(byte[] reusableBuffer, int count, NetEndPoint remoteEndPoint) { #if STATS_ENABLED Statistics.PacketsReceived++; Statistics.BytesReceived += (uint)count; #endif //Try read packet NetPacket packet = _netPacketPool.GetAndRead(reusableBuffer, 0, count); if (packet == null) { NetUtils.DebugWriteError("[NM] DataReceived: bad!"); return; } //Check unconnected switch (packet.Property) { case PacketProperty.DiscoveryRequest: if (DiscoveryEnabled) { var netEvent = CreateEvent(NetEventType.DiscoveryRequest); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.DiscoveryResponse: { var netEvent = CreateEvent(NetEventType.DiscoveryResponse); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.UnconnectedMessage: if (UnconnectedMessagesEnabled) { var netEvent = CreateEvent(NetEventType.ReceiveUnconnected); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.NatIntroduction: case PacketProperty.NatIntroductionRequest: case PacketProperty.NatPunchMessage: { if (NatPunchEnabled) { NatPunchModule.ProcessMessage(remoteEndPoint, packet); } return; } } //Check normal packets NetPeer netPeer; //Check peers Monitor.Enter(_peers); int peersCount = _peers.Count; if (_peers.TryGetValue(remoteEndPoint, out netPeer)) { Monitor.Exit(_peers); //Send if (packet.Property == PacketProperty.Disconnect) { if (BitConverter.ToInt64(packet.RawData, 1) != netPeer.ConnectId) { //Old or incorrect disconnect _netPacketPool.Recycle(packet); return; } var netEvent = CreateEvent(NetEventType.Disconnect); netPeer.SetDisconnectedState(); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; EnqueueEvent(netEvent); _peers.Remove(netPeer.EndPoint); //do not recycle because no sense) } else if (packet.Property == PacketProperty.ConnectAccept) { if (netPeer.ProcessConnectAccept(packet)) { var connectEvent = CreateEvent(NetEventType.Connect); connectEvent.Peer = netPeer; EnqueueEvent(connectEvent); } _netPacketPool.Recycle(packet); } else { netPeer.ProcessPacket(packet); } return; } try { if (peersCount < _maxConnections && packet.Property == PacketProperty.ConnectRequest) { int protoId = BitConverter.ToInt32(packet.RawData, 1); if (protoId != NetConstants.ProtocolId) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject. Invalid protocol ID: " + protoId); return; } string peerKey = Encoding.UTF8.GetString(packet.RawData, 13, packet.Size - 13); if (peerKey != ConnectKey) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject. Invalid key: " + peerKey); return; } //Getting new id for peer long connectionId = BitConverter.ToInt64(packet.RawData, 5); //response with id netPeer = new NetPeer(this, remoteEndPoint, connectionId); NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Received peer connect request Id: {0}, EP: {1}", netPeer.ConnectId, remoteEndPoint); //clean incoming packet _netPacketPool.Recycle(packet); _peers.Add(remoteEndPoint, netPeer); var netEvent = CreateEvent(NetEventType.Connect); netEvent.Peer = netPeer; EnqueueEvent(netEvent); } } finally { Monitor.Exit(_peers); } }
//Update function private void UpdateLogic() { #if DEBUG if (SimulateLatency) { var time = DateTime.UtcNow; lock (_pingSimulationList) { for (int i = 0; i < _pingSimulationList.Count; i++) { var incomingData = _pingSimulationList[i]; if (incomingData.TimeWhenGet <= time) { DataReceived(incomingData.Data, incomingData.Data.Length, incomingData.EndPoint); _pingSimulationList.RemoveAt(i); i--; } } } } #endif #if STATS_ENABLED ulong totalPacketLoss = 0; #endif //Process acks lock (_peers) { int delta = _logicThread.SleepTime; for (int i = 0; i < _peers.Count; i++) { var netPeer = _peers[i]; if (netPeer.ConnectionState == ConnectionState.Connected && netPeer.TimeSinceLastPacket > DisconnectTimeout) { NetUtils.DebugWrite("[NM] Disconnect by timeout: {0} > {1}", netPeer.TimeSinceLastPacket, DisconnectTimeout); var netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DisconnectReason = DisconnectReason.Timeout; netPeer.SetDisconnectedState(); EnqueueEvent(netEvent); RemovePeerAt(i); i--; } else if (netPeer.ConnectionState == ConnectionState.Disconnected) { var netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DisconnectReason = DisconnectReason.ConnectionFailed; EnqueueEvent(netEvent); RemovePeerAt(i); i--; } else { netPeer.Update(delta); #if STATS_ENABLED totalPacketLoss += netPeer.Statistics.PacketLoss; #endif } } } #if STATS_ENABLED Statistics.PacketLoss = totalPacketLoss; #endif }
internal void Update(int deltaTime) { if (_connectionState == ConnectionState.Disconnected) { return; } _timeSinceLastPacket += deltaTime; if (_connectionState == ConnectionState.InProgress) { _connectTimer += deltaTime; if (_connectTimer > _peerListener.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > _peerListener.MaxConnectAttempts) { _connectionState = ConnectionState.Disconnected; return; } //else send connect again SendConnectRequest(); } return; } //DebugWrite("[UPDATE]Delta: {0}ms, MaxSend: {1}", deltaTime, currentMaxSend); //Pending acks _reliableOrderedChannel.SendAcks(); _reliableUnorderedChannel.SendAcks(); //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= _peerListener.PingInterval) { NetUtils.DebugWrite("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping CreateAndSend(PacketProperty.Ping, _pingSequence); //reset timer _pingTimeStart = DateTime.UtcNow; } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= RttResetDelay) { _rttResetTimer = 0; //Rtt update _rtt = _avgRtt; _ping = _avgRtt; _peerListener.ConnectionLatencyUpdated(this, _ping); _rttCount = 1; } //MTU - Maximum transmission unit if (!_finishMtu) { _mtuCheckTimer += deltaTime; if (_mtuCheckTimer >= MtuCheckDelay) { _mtuCheckTimer = 0; _mtuCheckAttempts++; if (_mtuCheckAttempts >= MaxMtuCheckAttempts) { _finishMtu = true; } else { lock (_mtuMutex) { //Send increased packet if (_mtuIdx < NetConstants.PossibleMtu.Length - 1) { int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1] - NetConstants.HeaderSize; var p = _packetPool.Get(PacketProperty.MtuCheck, newMtu); p.RawData[1] = (byte)(_mtuIdx + 1); SendPacket(p); } } } } } //MTU - end //Pending send lock (_flushLock) { SendQueuedPackets(); } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { _timeSinceLastPacket = 0; NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.ConnectRequest: //response with connect long newId = BitConverter.ToInt64(packet.RawData, NetConstants.RequestConnectIdIndex); if (newId > _connectId) { _connectId = newId; } NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", ConnectId, newId, _remoteEndPoint); SendConnectAccept(); _packetPool.Recycle(packet); break; case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = _packetPool.GetAndRead(packet.RawData, pos, size); if (mergedPacket == null) { _packetPool.Recycle(packet); break; } pos += size; ProcessPacket(mergedPacket); } break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remotePingSequence) < 0) { _packetPool.Recycle(packet); break; } NetUtils.DebugWrite("[PP]Ping receive, send pong"); _remotePingSequence = packet.Sequence; _packetPool.Recycle(packet); //send CreateAndSend(PacketProperty.Pong, _remotePingSequence); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pingSequence) < 0) { _packetPool.Recycle(packet); break; } _pingSequence = packet.Sequence; int rtt = (int)(DateTime.UtcNow - _pingTimeStart).TotalMilliseconds; UpdateRoundTripTime(rtt); NetUtils.DebugWrite("[PP]Ping: {0}", rtt); _packetPool.Recycle(packet); break; //Process ack case PacketProperty.AckReliable: _reliableUnorderedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; case PacketProperty.AckReliableOrdered: _reliableOrderedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; //Process in order packets case PacketProperty.Sequenced: _sequencedChannel.ProcessPacket(packet); break; case PacketProperty.Reliable: _reliableUnorderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableOrdered: _reliableOrderedChannel.ProcessPacket(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property); break; } }
internal void AddIncomingPacket(NetPacket p) { if (p.IsFragmented) { NetUtils.DebugWrite("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal); //Get needed array from dictionary ushort packetFragId = p.FragmentId; IncomingFragments incomingFragments; if (!_holdedFragments.TryGetValue(packetFragId, out incomingFragments)) { incomingFragments = new IncomingFragments { Fragments = new NetPacket[p.FragmentsTotal] }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null) { _packetPool.Recycle(p); NetUtils.DebugWriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size int dataOffset = p.GetHeaderSize() + NetConstants.FragmentHeaderSize; incomingFragments.TotalSize += p.Size - dataOffset; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) { return; } NetUtils.DebugWrite("Received all fragments!"); NetPacket resultingPacket = _packetPool.Get(p.Property, incomingFragments.TotalSize); int resultingPacketOffset = resultingPacket.GetHeaderSize(); int firstFragmentSize = fragments[0].Size - dataOffset; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { //Create resulting big packet int fragmentSize = fragments[i].Size - dataOffset; Buffer.BlockCopy( fragments[i].RawData, dataOffset, resultingPacket.RawData, resultingPacketOffset + firstFragmentSize * i, fragmentSize); //Free memory _packetPool.Recycle(fragments[i]); fragments[i] = null; } //Send to process _peerListener.ReceiveFromPeer(resultingPacket, _remoteEndPoint); //Clear memory _packetPool.Recycle(resultingPacket); _holdedFragments.Remove(packetFragId); } else //Just simple packet { _peerListener.ReceiveFromPeer(p, _remoteEndPoint); _packetPool.Recycle(p); } }
/// <summary> /// Send data to peer /// </summary> /// <param name="data">Data</param> /// <param name="start">Start of data</param> /// <param name="length">Length of data</param> /// <param name="options">Send options (reliable, unreliable, etc.)</param> public void Send(byte[] data, int start, int length, SendOptions options) { if (_connectionState == ConnectionState.Disconnected) { return; } //Prepare PacketProperty property = SendOptionsToProperty(options); int headerSize = NetPacket.GetHeaderSize(property); int mtu = _mtu; //Check fragmentation if (length + headerSize > mtu) { if (options == SendOptions.Sequenced || options == SendOptions.Unreliable) { throw new Exception("Unreliable packet size > allowed (" + (mtu - headerSize) + ")"); } int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int fullPacketsCount = length / packetDataSize; int lastPacketSize = length % packetDataSize; int totalPackets = fullPacketsCount + (lastPacketSize == 0 ? 0 : 1); NetUtils.DebugWrite("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " fullPacketsCount: {4}\n" + " lastPacketSize: {5}\n" + " totalPackets: {6}", mtu, headerSize, packetFullSize, packetDataSize, fullPacketsCount, lastPacketSize, totalPackets); if (totalPackets > ushort.MaxValue) { throw new Exception("Too many fragments: " + totalPackets + " > " + ushort.MaxValue); } int dataOffset = headerSize + NetConstants.FragmentHeaderSize; lock (_sendLock) { for (ushort i = 0; i < fullPacketsCount; i++) { NetPacket p = _packetPool.Get(property, packetFullSize); p.FragmentId = _fragmentId; p.FragmentPart = i; p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize); SendPacket(p); } if (lastPacketSize > 0) { NetPacket p = _packetPool.Get(property, lastPacketSize + NetConstants.FragmentHeaderSize); p.FragmentId = _fragmentId; p.FragmentPart = (ushort)fullPacketsCount; //last p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, fullPacketsCount * packetDataSize, p.RawData, dataOffset, lastPacketSize); SendPacket(p); } _fragmentId++; } return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); SendPacket(packet); }