/// <summary> /// Flush all queued packets /// </summary> public void Flush() { lock (_flushLock) { _reliableOrderedChannel.SendNextPackets(); _reliableUnorderedChannel.SendNextPackets(); _reliableSequencedChannel.SendNextPackets(); _sequencedChannel.SendNextPackets(); _unreliableChannel.SendNextPackets(); //If merging enabled if (_mergePos > 0) { if (_mergeCount > 1) { NetUtils.DebugWrite("Send merged: " + _mergePos + ", count: " + _mergeCount); _netManager.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, _remoteEndPoint); #if STATS_ENABLED Statistics.PacketsSent++; Statistics.BytesSent += (ulong)(NetConstants.HeaderSize + _mergePos); #endif } else { //Send without length information and merging _netManager.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, _remoteEndPoint); #if STATS_ENABLED Statistics.PacketsSent++; Statistics.BytesSent += (ulong)(_mergePos - 2); #endif } _mergePos = 0; _mergeCount = 0; } } }
internal void ReceiveFromPeer(NetPacket packet, IPEndPoint remoteEndPoint) { NetPeer fromPeer; if (peers.TryGetValue(remoteEndPoint, out fromPeer)) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Received message"); var netEvent = CreateEvent(NetEventType.Receive); netEvent.Peer = fromPeer; netEvent.RemoteEndPoint = fromPeer.EndPoint; switch (packet.Property) { case PacketProperty.Unreliable: netEvent.DeliveryMethod = DeliveryMethod.Unreliable; break; case PacketProperty.ReliableUnordered: netEvent.DeliveryMethod = DeliveryMethod.ReliableUnordered; break; case PacketProperty.ReliableOrdered: netEvent.DeliveryMethod = DeliveryMethod.ReliableOrdered; break; case PacketProperty.Sequenced: netEvent.DeliveryMethod = DeliveryMethod.Sequenced; break; case PacketProperty.ReliableSequenced: //TODO: netEvent.DeliveryMethod = DeliveryMethod.ReliableSequenced; break; } netEvent.DataReader.SetSource(packet.CopyPacketData()); EnqueueEvent(netEvent); } }
private NetPeer OnConnectionSolved(ConnectionRequest request) { if (request.Result == ConnectionRequestResult.Reject) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject."); } else if (GetPeersCount(ConnectionState.Connected | ConnectionState.InProgress) < maxConnections) { NetPeer netPeer = null; lock (connectingPeers) { if (connectingPeers.Remove(request.RemoteEndPoint)) { //response with id netPeer = new NetPeer(this, request.RemoteEndPoint, request.ConnectionId); NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Received peer connection Id: {0}, EP: {1}", netPeer.ConnectId, netPeer.EndPoint); //add peer to list peers.Add(request.RemoteEndPoint, netPeer); } } if (netPeer != null) { var netEvent = CreateEvent(NetEventType.Connect); netEvent.Peer = netPeer; EnqueueEvent(netEvent); return(netPeer); } return(null); } lock (connectingPeers) connectingPeers.Remove(request.RemoteEndPoint); return(null); }
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.GetWithProperty(PacketProperty.MtuOk, 1); mtuOkPacket.RawData[1] = packet.RawData[1]; _netManager.SendRawAndRecycle(mtuOkPacket, _remoteEndPoint); } 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); } }
//ProcessAck in packet public void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { NetUtils.DebugWrite("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { NetUtils.DebugWrite("[PA]Bad window start"); return; } //check relevance if (windowRel >= _windowSize) { NetUtils.DebugWrite("[PA]Old acks"); return; } byte[] acksData = packet.RawData; Monitor.Enter(_pendingPackets);; for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); if (rel >= _windowSize) { NetUtils.DebugWrite("[PA]REL: " + rel); break; } int pendingIdx = pendingSeq % _windowSize; int currentByte = NetConstants.SequencedHeaderSize + pendingIdx / BitsInByte; int currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if STATS_ENABLED || DEBUG Peer.Statistics.PacketLoss++; #endif //Skip false ack NetUtils.DebugWrite("[PA]False ack: {0}", pendingSeq); continue; } if (pendingSeq == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } //clear packet var pendingPacket = _pendingPackets[pendingIdx]; if (pendingPacket.Packet == null) { continue; } Peer.Recycle(pendingPacket.Packet); pendingPacket.Packet = null; NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); } Monitor.Exit(_pendingPackets); }
//Process incoming packet public override void ProcessPacket(NetPacket packet) { int seq = packet.Sequence; if (seq >= NetConstants.MaxSequence) { NetUtils.DebugWrite("[RR]Bad sequence"); return; } int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(seq, _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 int ackIdx; int ackByte; int ackBit; Monitor.Enter(_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.SequencedHeaderSize + 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.SequencedHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { NetUtils.DebugWrite("[RR]ReliableInOrder duplicate"); Monitor.Exit(_outgoingAcks); return; } //save ack _outgoingAcks.RawData[ackByte] |= (byte)(1 << ackBit); Monitor.Exit(_outgoingAcks); //detailed check if (seq == _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[ackIdx] = packet; } else { _earlyReceived[ackIdx] = true; Peer.AddIncomingPacket(packet); } }
private void DataReceived(byte[] reusableBuffer, int count, IPEndPoint remoteEndPoint) { #if STATS_ENABLED Statistics.PacketsReceived++; Statistics.BytesReceived += (uint)count; #endif //Try read packet NetPacket packet = NetPacketPool.GetPacket(count, false); if (!packet.FromBytes(reusableBuffer, 0, count)) { NetPacketPool.Recycle(packet); 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; bool isPeerConnecting; lock (connectingPeers) { isPeerConnecting = connectingPeers.Contains(remoteEndPoint); peers.TryGetValue(remoteEndPoint, out netPeer); } if (netPeer != null) { if (netPeer.ConnectionState == ConnectionState.Disconnected) { return; } NetEvent netEvent; switch (packet.Property) { case PacketProperty.Disconnect: if (netPeer.ConnectionState == ConnectionState.InProgress || netPeer.ConnectionState == ConnectionState.Connected) { if (BitConverter.ToInt64(packet.RawData, 1) != netPeer.ConnectId) { //Old or incorrect disconnect NetPacketPool.Recycle(packet); return; } netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; EnqueueEvent(netEvent); netPeer.ProcessPacket(packet); SendRaw(new[] { (byte)PacketProperty.ShutdownOk }, 0, 1, remoteEndPoint); } return; case PacketProperty.ConnectAccept: if (netPeer.ProcessConnectAccept(packet)) { var connectEvent = CreateEvent(NetEventType.Connect); connectEvent.Peer = netPeer; EnqueueEvent(connectEvent); } return; case PacketProperty.ConnectRequest: long newId = BitConverter.ToInt64(packet.RawData, NetConstants.RequestConnectIdIndex); NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", netPeer.ConnectId, newId, remoteEndPoint); //Remove old peer and connect new if (newId > netPeer.ConnectId) { netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; peers.RemovePeer(netPeer); break; //To reconnect peer } else { //Just answer accept netPeer.ProcessPacket(packet); return; } default: netPeer.ProcessPacket(packet); return; } } //Unacked shutdown if (packet.Property == PacketProperty.Disconnect) { SendRaw(new[] { (byte)PacketProperty.ShutdownOk }, 0, 1, remoteEndPoint); return; } if (packet.Property == PacketProperty.ConnectRequest && packet.Size >= 12) { NetUtils.DebugWrite("[NM] Received ConnectionRequest"); if (isPeerConnecting) { NetUtils.DebugWrite("[NM] Peer already connecting"); return; } if (GetPeersCount(ConnectionState.Connected | ConnectionState.InProgress) < maxConnections) { int protoId = BitConverter.ToInt32(packet.RawData, 1); if (protoId != NetConstants.ProtocolId) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject. Invalid protocol ID: " + protoId); return; } //Getting new id for peer long connectionId = BitConverter.ToInt64(packet.RawData, 5); // Read data and create request var reader = new NetDataReader(null, 0, 0); if (packet.Size > 12) { reader.SetSource(packet.RawData, 13, packet.Size); } NetUtils.DebugWrite("[NM] Creating request event: " + connectionId); lock (connectingPeers) { connectingPeers.Add(remoteEndPoint); } var netEvent = CreateEvent(NetEventType.ConnectionRequest); netEvent.ConnectionRequest = new ConnectionRequest(connectionId, remoteEndPoint, reader, OnConnectionSolved); EnqueueEvent(netEvent); } } }
internal void Update(int deltaTime) { _timeSinceLastPacket += deltaTime; switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > _netManager.DisconnectTimeout) { NetUtils.DebugWrite( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, _netManager.DisconnectTimeout); _netManager.DisconnectPeer(this, DisconnectReason.Timeout, 0, true, null, 0, 0); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > _netManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _netManager.SendRaw(_shutdownPacket, _remoteEndPoint); } return; case ConnectionState.InProgress: _connectTimer += deltaTime; if (_connectTimer > _netManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > _netManager.MaxConnectAttempts) { _netManager.DisconnectPeer(this, DisconnectReason.ConnectionFailed, 0, true, null, 0, 0); return; } //else send connect again _netManager.SendRaw(_connectRequestPacket, _remoteEndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= _netManager.PingInterval) { NetUtils.DebugWrite("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence = _pingSequence; SendRawData(_pingPacket); //reset timer _pingTimeStart = DateTime.UtcNow; } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= RttResetDelay) { _rttResetTimer = 0; //Rtt update _rtt = _avgRtt; _ping = _avgRtt; _netManager.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.GetWithProperty(PacketProperty.MtuCheck, newMtu); p.RawData[1] = (byte)(_mtuIdx + 1); //Must check result for MTU fix if (!_netManager.SendRawAndRecycle(p, _remoteEndPoint)) { _finishMtu = true; } } } } } } //MTU - end //Pending send Flush(); }
//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 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.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; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; case PacketProperty.ShutdownOk: case PacketProperty.Disconnect: _connectionState = ConnectionState.Disconnected; _packetPool.Recycle(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.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 _packetPool.Recycle(resultingPacket); _holdedFragments.Remove(packetFragId); } else //Just simple packet { _netManager.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> /// <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) { 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.IsFragmented = true; 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.IsFragmented = true; 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); }