public void AddToQueue(NetPacket packet) { lock (_outgoingPackets) { _outgoingPackets.Enqueue(packet); } }
public NetPacket GetAndClear() { var packet = Packet; Packet = null; TimeStamp = null; return packet; }
public void ProcessPacket(NetPacket packet) { if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence) > 0) { _remoteSequence = packet.Sequence; _peer.AddIncomingPacket(packet); } }
internal void AddIncomingPacket(NetPacket p) { if (p.IsFragmented) { 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; //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size int dataOffset = p.GetHeaderSize() + NetConstants.FragmentHeaderSize; incomingFragments.TotalSize += p.RawData.Length - dataOffset; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) return; DebugWrite("Received all fragments!"); NetPacket resultingPacket = GetPacketFromPool(p.Property, incomingFragments.TotalSize); int resultingPacketOffset = resultingPacket.GetHeaderSize(); int firstFragmentSize = fragments[0].RawData.Length - dataOffset; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { //Create resulting big packet int fragmentSize = fragments[i].RawData.Length - dataOffset; Buffer.BlockCopy( fragments[i].RawData, dataOffset, resultingPacket.RawData, resultingPacketOffset + firstFragmentSize * i, fragmentSize); //Free memory Recycle(fragments[i]); fragments[i] = null; } //Send to process _peerListener.ReceiveFromPeer(resultingPacket, _remoteEndPoint); //Clear memory Recycle(resultingPacket); _holdedFragments.Remove(packetFragId); } else //Just simple packet { _peerListener.ReceiveFromPeer(p, _remoteEndPoint); Recycle(p); } }
private void ProcessMtuPacket(NetPacket packet) { if (packet.RawData.Length == 1 || packet.RawData[1] >= NetConstants.PossibleMtu.Length) return; //MTU auto increase if (packet.Property == PacketProperty.MtuCheck) { if (packet.RawData.Length != NetConstants.PossibleMtu[packet.RawData[1]]) { return; } _mtuCheckAttempts = 0; DebugWrite("MTU check. Resend: " + packet.RawData[1]); var mtuOkPacket = GetPacketFromPool(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; } DebugWrite("MTU ok. Increase to: " + _mtu); } }
/// <summary> /// Gets maximum size of packet that will be not fragmented. /// </summary> /// <param name="options">Type of packet that you want send</param> /// <returns>size in bytes</returns> public int GetMaxSinglePacketSize(DeliveryMethod options) { return(_mtu - NetPacket.GetHeaderSize(SendOptionsToProperty(options))); }
internal void Recycle(NetPacket packet) { packet.RawData = null; lock (_packetPool) { _packetPool.Push(packet); } }
/// <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> /// <exception cref="TooBigPacketException"> /// If size exceeds maximum limit:<para/> /// MTU - headerSize bytes for Unreliable<para/> /// Fragment count exceeded ushort.MaxValue<para/> /// </exception> public void Send(byte[] data, int start, int length, DeliveryMethod options) { if (_connectionState == ConnectionState.ShutdownRequested || _connectionState == ConnectionState.Disconnected) { return; } //Prepare PacketProperty property = SendOptionsToProperty(options); NetUtils.DebugWrite("[RS]Packet: " + property); //Select channel BaseChannel channel; switch (property) { case PacketProperty.ReliableUnordered: channel = _reliableUnorderedChannel; break; case PacketProperty.Sequenced: channel = _sequencedChannel; break; case PacketProperty.ReliableOrdered: channel = _reliableOrderedChannel; break; case PacketProperty.Unreliable: channel = _unreliableChannel; break; case PacketProperty.ReliableSequenced: channel = _reliableSequencedChannel; break; default: throw new InvalidPacketException("Unknown packet property: " + property); } //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { if (options == DeliveryMethod.Sequenced || options == DeliveryMethod.Unreliable || options == DeliveryMethod.ReliableSequenced) { throw new TooBigPacketException("Unreliable packet size exceeded maximum of " + (_mtu - headerSize) + " bytes"); } 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 TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); } int dataOffset = headerSize + NetConstants.FragmentHeaderSize; lock (_sendLock) { for (ushort i = 0; i < fullPacketsCount; i++) { NetPacket p = _packetPool.GetWithProperty(property, packetFullSize); p.FragmentId = _fragmentId; p.FragmentPart = i; p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize); channel.AddToQueue(p); } if (lastPacketSize > 0) { NetPacket p = _packetPool.GetWithProperty(property, lastPacketSize + NetConstants.FragmentHeaderSize); p.FragmentId = _fragmentId; p.FragmentPart = (ushort)fullPacketsCount; //last p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, fullPacketsCount * packetDataSize, p.RawData, dataOffset, lastPacketSize); channel.AddToQueue(p); } _fragmentId++; } return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); channel.AddToQueue(packet); }
internal override void ReceiveFromPeer(NetPacket packet, NetEndPoint remoteEndPoint) { NetPeer fromPeer; if (_peers.TryGetValue(remoteEndPoint, out fromPeer)) { var netEvent = CreateEvent(NetEventType.Receive); netEvent.Peer = fromPeer; netEvent.RemoteEndPoint = fromPeer.EndPoint; netEvent.DataReader.SetSource(packet.GetPacketData()); EnqueueEvent(netEvent); } }
//Process incoming packet public void ProcessPacket(NetPacket packet) { if (packet.Sequence >= NetConstants.MaxSequence) { _peer.DebugWrite("[RR]Bad sequence"); return; } int relate = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); if (relateSeq > _windowSize) { _peer.DebugWrite("[RR]Bad sequence"); return; } //Drop bad packets if(relate < 0) { //Too old packet doesn't ack _peer.DebugWrite("[RR]ReliableInOrder too old"); return; } if (relate >= _windowSize * 2) { //Some very new packet _peer.DebugWrite("[RR]ReliableInOrder too new"); return; } //If very new - move window Monitor.Enter(_outgoingAcksAccess); 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]) { _peer.DebugWrite("[RR]ReliableInOrder duplicate"); Monitor.Exit(_outgoingAcksAccess); return; } //save ack _outgoingAcks[packet.Sequence % _windowSize] = true; Monitor.Exit(_outgoingAcksAccess); //detailed check if (packet.Sequence == _remoteSequence) { _peer.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); } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { _timeSinceLastPacket = 0; if (packet.ConnectionNumber != _connectNum && packet.Property != PacketProperty.ShutdownOk) //withou connectionNum { NetUtils.DebugWrite(ConsoleColor.Red, "[RR]Old packet"); _packetPool.Recycle(packet); return; } NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = _packetPool.GetPacket(size, false); if (!mergedPacket.FromBytes(packet.RawData, pos, size)) { _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 _pongPacket.Sequence = _remotePingSequence; _netManager.SendRaw(_pongPacket, _remoteEndPoint); 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.ReliableUnordered: _reliableUnorderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableOrdered: _reliableOrderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableSequenced: _reliableSequencedChannel.ProcessPacket(packet); break; case PacketProperty.AckReliableSequenced: _reliableSequencedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; case PacketProperty.ShutdownOk: if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } _packetPool.Recycle(packet); break; default: NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property); break; } }
internal void Update(int deltaTime) { Interlocked.Add(ref _timeSinceLastPacket, deltaTime); switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { NetDebug.Write( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, NetManager.DisconnectTimeout); NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, 0, null); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _shutdownTimer += deltaTime; if (_shutdownTimer >= ShutdownDelay) { _shutdownTimer = 0; NetManager.SendRaw(_shutdownPacket, EndPoint); } } return; case ConnectionState.Outgoing: _connectTimer += deltaTime; if (_connectTimer > NetManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > NetManager.MaxConnectAttempts) { NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, 0, null); return; } //else send connect again NetManager.SendRaw(_connectRequestPacket, EndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= NetManager.PingInterval) { NetDebug.Write("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence++; //ping timeout if (_pingTimer.IsRunning) { UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); } _pingTimer.Reset(); _pingTimer.Start(); NetManager.SendRaw(_pingPacket, EndPoint); } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= NetManager.PingInterval * 3) { _rttResetTimer = 0; _rtt = _avgRtt; _rttCount = 1; } UpdateMtuLogic(deltaTime); //Pending send BaseChannel currentChannel = _headChannel; while (currentChannel != null) { currentChannel.SendNextPackets(); currentChannel = currentChannel.Next; } lock (_unreliableChannel) { while (_unreliableChannel.Count > 0) { NetPacket packet = _unreliableChannel.Dequeue(); SendUserData(packet); NetManager.NetPacketPool.Recycle(packet); } } SendMerged(); }
/// <summary> /// Gets maximum size of packet that will be not fragmented. /// </summary> /// <param name="options">Type of packet that you want send</param> /// <returns>size in bytes</returns> public int GetMaxSinglePacketSize(DeliveryMethod options) { return(_mtu - NetPacket.GetHeaderSize(options == DeliveryMethod.Unreliable ? PacketProperty.Unreliable : PacketProperty.Channeled)); }
//Process incoming packet public override bool ProcessPacket(NetPacket packet) { if (packet.Property == PacketProperty.Ack) { ProcessAck(packet); return(false); } int seq = packet.Sequence; if (seq >= NetConstants.MaxSequence) { NetDebug.Write("[RR]Bad sequence"); return(false); } int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); if (relateSeq > _windowSize) { NetDebug.Write("[RR]Bad sequence"); return(false); } //Drop bad packets if (relate < 0) { //Too old packet doesn't ack NetDebug.Write("[RR]ReliableInOrder too old"); return(false); } if (relate >= _windowSize * 2) { //Some very new packet NetDebug.Write("[RR]ReliableInOrder too new"); return(false); } //If very new - move window int ackIdx; int ackByte; int ackBit; lock (_outgoingAcks) { if (relate >= _windowSize) { //New window position int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; _outgoingAcks.Sequence = (ushort)newWindowStart; //Clean old data while (_remoteWindowStart != newWindowStart) { ackIdx = _remoteWindowStart % _windowSize; ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; _outgoingAcks.RawData[ackByte] &= (byte)~(1 << ackBit); _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; } } //Final stage - process valid packet //trigger acks send _mustSendAcks = true; ackIdx = seq % _windowSize; ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { NetDebug.Write("[RR]ReliableInOrder duplicate"); return(false); } //save ack _outgoingAcks.RawData[ackByte] |= (byte)(1 << ackBit); } //detailed check if (seq == _remoteSequence) { NetDebug.Write("[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(true); } //holded packet if (_ordered) { _receivedPackets[ackIdx] = packet; } else { _earlyReceived[ackIdx] = true; Peer.AddIncomingPacket(packet); } return(true); }
public void Init(NetPacket packet) { _packet = packet; _isSent = false; }
//ProcessAck in packet private void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { NetDebug.Write("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { NetDebug.Write("[PA]Bad window start"); return; } //check relevance if (windowRel >= _windowSize) { NetDebug.Write("[PA]Old acks"); return; } byte[] acksData = packet.RawData; lock (_pendingPackets) { for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); if (rel >= _windowSize) { NetDebug.Write("[PA]REL: " + rel); break; } int pendingIdx = pendingSeq % _windowSize; int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte; int currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if STATS_ENABLED || DEBUG Peer.Statistics.PacketLoss++; #endif //Skip false ack NetDebug.Write("[PA]False ack: {0}", pendingSeq); continue; } if (pendingSeq == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } //clear packet if (_pendingPackets[pendingIdx].Clear(Peer)) { NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); } } } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { _lastPacketReceivedStart = DateTime.UtcNow; DebugWrite("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.RawData.Length) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = GetPacketFromPool(init: false); if (!mergedPacket.FromBytes(packet.RawData, pos, size)) { Recycle(packet); break; } pos += size; ProcessPacket(mergedPacket); } break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remotePingSequence) < 0) { Recycle(packet); break; } DebugWrite("[PP]Ping receive, send pong"); _remotePingSequence = packet.Sequence; 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) { Recycle(packet); break; } _pingSequence = packet.Sequence; int rtt = (int)(DateTime.UtcNow - _pingTimeStart).TotalMilliseconds; UpdateRoundTripTime(rtt); DebugWrite("[PP]Ping: {0}", rtt); Recycle(packet); break; //Process ack case PacketProperty.AckReliable: _reliableUnorderedChannel.ProcessAck(packet); Recycle(packet); break; case PacketProperty.AckReliableOrdered: _reliableOrderedChannel.ProcessAck(packet); 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: DebugWriteForce("Error! Unexpected packet type: " + packet.Property); break; } }
internal abstract void ReceiveFromPeer(NetPacket packet, NetEndPoint endPoint);
private void SendInternal( byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) { return; } //Select channel PacketProperty property; BaseChannel channel = null; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; } else { property = PacketProperty.Channeled; channel = CreateChannel((byte)(channelNumber * 4 + (byte)deliveryMethod)); } //Prepare NetDebug.Write("[RS]Packet: " + property); //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { //if cannot be fragmented if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) { throw new TooBigPacketException("Unreliable packet size exceeded maximum of " + (mtu - headerSize) + " bytes"); } int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); NetDebug.Write("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " totalPackets: {4}", mtu, headerSize, packetFullSize, packetDataSize, totalPackets); if (totalPackets > ushort.MaxValue) { throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); } ushort currentFragmentId = (ushort)Interlocked.Increment(ref _fragmentId); for (ushort partIdx = 0; partIdx < totalPackets; partIdx++) { int sendLength = length > packetDataSize ? packetDataSize : length; NetPacket p = _packetPool.GetPacket(headerSize + sendLength + NetConstants.FragmentHeaderSize); p.Property = property; p.UserData = userData; p.FragmentId = currentFragmentId; p.FragmentPart = partIdx; p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, partIdx * packetDataSize, p.RawData, NetConstants.FragmentedHeaderTotalSize, sendLength); channel.AddToQueue(p); length -= sendLength; } return; } //Else just send NetPacket packet = _packetPool.GetPacket(headerSize + length); packet.Property = property; Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); packet.UserData = userData; if (channel == null) //unreliable { lock (_unreliableChannel) _unreliableChannel.Enqueue(packet); } else { channel.AddToQueue(packet); } }
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.GetWithProperty(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 _netManager.ReceiveFromPeer(resultingPacket, _remoteEndPoint); //Clear memory _holdedFragments.Remove(packetFragId); } else //Just simple packet { _netManager.ReceiveFromPeer(p, _remoteEndPoint); } }
//ProcessAck in packet public void ProcessAck(NetPacket packet) { int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize; if (packet.RawData.Length != validPacketSize) { _peer.DebugWriteForce("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; if (ackWindowStart > NetConstants.MaxSequence) { _peer.DebugWrite("[PA]Bad window start"); return; } //check relevance if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize) { _peer.DebugWrite("[PA]Old acks"); return; } byte[] acksData = packet.RawData; _peer.DebugWrite("[PA]AcksStart: {0}", ackWindowStart); int startByte = NetConstants.SequencedHeaderSize; Monitor.Enter(_pendingPacketsAccess); for (int i = 0; i < _windowSize; i++) { int ackSequence = (ackWindowStart + i) % NetConstants.MaxSequence; if (NetUtils.RelativeSequenceNumber(ackSequence, _localWindowStart) < 0) { //Skip old ack continue; } int currentByte = startByte + i / BitsInByte; int currentBit = i % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { //Skip false ack continue; } if (ackSequence == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } int storeIdx = ackSequence % _windowSize; if (_pendingPackets[storeIdx].NotEmpty) { NetPacket removed = _pendingPackets[storeIdx].GetAndClear(); _peer.Recycle(removed); _peer.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", ackSequence); } else { _peer.DebugWrite("[PA]Removing reliableInOrder ack: {0} - false", ackSequence); } } Monitor.Exit(_pendingPacketsAccess); }
internal void AddReliablePacket(DeliveryMethod method, NetPacket p) { if (p.IsFragmented) { NetDebug.Write("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], ChannelId = p.ChannelId }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null || p.ChannelId != incomingFragments.ChannelId) { _packetPool.Recycle(p); NetDebug.WriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size incomingFragments.TotalSize += p.Size - NetConstants.FragmentedHeaderTotalSize; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) { return; } //just simple packet NetPacket resultingPacket = _packetPool.GetPacket(incomingFragments.TotalSize); int firstFragmentSize = fragments[0].Size - NetConstants.FragmentedHeaderTotalSize; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { var fragment = fragments[i]; //Create resulting big packet Buffer.BlockCopy( fragment.RawData, NetConstants.FragmentedHeaderTotalSize, resultingPacket.RawData, firstFragmentSize * i, fragment.Size - NetConstants.FragmentedHeaderTotalSize); //Free memory _packetPool.Recycle(fragment); } Array.Clear(fragments, 0, incomingFragments.ReceivedCount); //Send to process NetManager.CreateReceiveEvent(resultingPacket, method, 0, this); //Clear memory _holdedFragments.Remove(packetFragId); } else //Just simple packet { NetManager.CreateReceiveEvent(p, method, NetConstants.ChanneledHeaderSize, this); } }
internal override void ReceiveFromPeer(NetPacket packet, NetEndPoint remoteEndPoint) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NC] Received message"); var netEvent = CreateEvent(NetEventType.Receive); netEvent.DataReader.SetSource(packet.GetPacketData()); netEvent.Peer = _peer; netEvent.RemoteEndPoint = remoteEndPoint; EnqueueEvent(netEvent); }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { //not initialized if (_connectionState == ConnectionState.Outgoing || _connectionState == ConnectionState.Disconnected) { _packetPool.Recycle(packet); return; } if (packet.Property == PacketProperty.ShutdownOk) { if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } _packetPool.Recycle(packet); return; } if (packet.ConnectionNumber != _connectNum) { NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet"); _packetPool.Recycle(packet); return; } Interlocked.Exchange(ref _timeSinceLastPacket, 0); NetDebug.Write("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = _packetPool.GetPacket(size); if (!mergedPacket.FromBytes(packet.RawData, pos, size)) { _packetPool.Recycle(packet); break; } pos += size; ProcessPacket(mergedPacket); } break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0) { NetDebug.Write("[PP]Ping receive, send pong"); FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); _pongPacket.Sequence = packet.Sequence; NetManager.SendRaw(_pongPacket, EndPoint); } _packetPool.Recycle(packet); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (packet.Sequence == _pingPacket.Sequence) { _pingTimer.Stop(); int elapsedMs = (int)_pingTimer.ElapsedMilliseconds; _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond) / 2 - DateTime.UtcNow.Ticks; UpdateRoundTripTime(elapsedMs); NetManager.ConnectionLatencyUpdated(this, elapsedMs / 2); NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta); } _packetPool.Recycle(packet); break; case PacketProperty.Ack: case PacketProperty.Channeled: if (packet.ChannelId > _channels.Length) { _packetPool.Recycle(packet); break; } var channel = _channels[packet.ChannelId] ?? (packet.Property == PacketProperty.Ack ? null : CreateChannel(packet.ChannelId)); if (channel != null) { if (!channel.ProcessPacket(packet)) { _packetPool.Recycle(packet); } } break; //Simple packet without acks case PacketProperty.Unreliable: NetManager.CreateReceiveEvent(packet, DeliveryMethod.Unreliable, NetConstants.HeaderSize, this); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); break; } }
//from user thread, our thread, or recv? private void SendPacket(NetPacket packet) { switch (packet.Property) { case PacketProperty.Reliable: DebugWrite("[RS]Packet reliable"); _reliableUnorderedChannel.AddToQueue(packet); break; case PacketProperty.Sequenced: DebugWrite("[RS]Packet sequenced"); _sequencedChannel.AddToQueue(packet); break; case PacketProperty.ReliableOrdered: DebugWrite("[RS]Packet reliable ordered"); _reliableOrderedChannel.AddToQueue(packet); break; case PacketProperty.Unreliable: DebugWrite("[RS]Packet simple"); _simpleChannel.AddToQueue(packet); break; case PacketProperty.MtuCheck: //Must check result for MTU fix if (!_peerListener.SendRaw(packet.RawData, 0, packet.RawData.Length, _remoteEndPoint)) { _finishMtu = true; } Recycle(packet); break; case PacketProperty.AckReliable: case PacketProperty.AckReliableOrdered: case PacketProperty.Ping: case PacketProperty.Pong: case PacketProperty.Disconnect: case PacketProperty.MtuOk: SendRawData(packet.RawData); Recycle(packet); break; default: throw new Exception("Unknown packet property: " + packet.Property); } }
internal NetPacket GetPacketFromPool(PacketProperty property = PacketProperty.Unreliable, int size=0, bool init=true) { NetPacket packet = null; lock (_packetPool) { if (_packetPool.Count > 0) { packet = _packetPool.Pop(); } } if(packet == null) { packet = new NetPacket(); } if(init) packet.Init(property, size); return packet; }
protected override void ReceiveFromSocket(byte[] reusableBuffer, int count, NetEndPoint remoteEndPoint) { NetPacket packet; NetPeer netPeer; //Check peers if (_peers.TryGetValue(remoteEndPoint, out netPeer)) { packet = netPeer.GetPacketFromPool(init: false); //Bad packet check if (!packet.FromBytes(reusableBuffer, 0, count)) { netPeer.Recycle(packet); return; } //Send if (packet.Property == PacketProperty.Disconnect) { if (BitConverter.ToUInt64(packet.RawData, 1) != _peerConnectionIds[remoteEndPoint]) { //Old or incorrect disconnect netPeer.Recycle(packet); return; } RemovePeer(netPeer); var netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; EnqueueEvent(netEvent); } else if (packet.Property == PacketProperty.ConnectRequest) //response with connect { ulong lastId = _peerConnectionIds[remoteEndPoint]; ulong newId = BitConverter.ToUInt64(packet.RawData, 1); if (newId > lastId) { _peerConnectionIds[remoteEndPoint] = newId; } NetUtils.DebugWrite(ConsoleColor.Cyan, "ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", lastId, newId, remoteEndPoint); SendConnectAccept(netPeer, _peerConnectionIds[remoteEndPoint]); netPeer.Recycle(packet); } else //throw out garbage packets { netPeer.ProcessPacket(packet); } return; } //Else add new peer packet = new NetPacket(); if (!packet.FromBytes(reusableBuffer, 0, count)) { //Bad packet return; } if (_peers.Count < _maxClients && packet.Property == PacketProperty.ConnectRequest) { string peerKey = Encoding.UTF8.GetString(packet.RawData, 9, packet.RawData.Length - 9); if (peerKey != _connectKey) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NS] Peer connect reject. Invalid key: " + peerKey); return; } //Getting new id for peer netPeer = CreatePeer(remoteEndPoint); //response with id ulong connectionId = BitConverter.ToUInt64(packet.RawData, 1); NetUtils.DebugWrite(ConsoleColor.Cyan, "[NS] Received peer connect request Id: {0}, EP: {1}", connectionId, remoteEndPoint); SendConnectAccept(netPeer, connectionId); //clean incoming packet netPeer.Recycle(packet); lock (_peers) { _peers.Add(remoteEndPoint, netPeer); _peerConnectionIds.Add(remoteEndPoint, connectionId); } var netEvent = CreateEvent(NetEventType.Connect); netEvent.Peer = netPeer; EnqueueEvent(netEvent); } }
//For channels internal void Recycle(NetPacket packet) { _packetPool.Recycle(packet); }