// on user thread
        private void SendFragmentedMessage(NetOutgoingMessage msg, IList <NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
        {
            // Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
            // this should be ok however; as long as recipients differentiate between same id but different sender
            int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);

            if (group >= NetConstants.MaxFragmentationGroups)
            {
                // @TODO: not thread safe; but in practice probably not an issue
                m_lastUsedFragmentGroup = 1;
                group = 1;
            }
            msg.m_fragmentGroup = group;

            // do not send msg; but set fragmentgroup in case user tries to recycle it immediately

            // create fragmentation specifics
            int totalBytes = msg.LengthBytes;

            // determine minimum mtu for all recipients
            int mtu           = GetMTU(recipients);
            int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);

            int numChunks = totalBytes / bytesPerChunk;

            if (numChunks * bytesPerChunk < totalBytes)
            {
                numChunks++;
            }

            int bitsPerChunk = bytesPerChunk * 8;
            int bitsLeft     = msg.LengthBits;

            for (int i = 0; i < numChunks; i++)
            {
                NetOutgoingMessage chunk = CreateMessage(mtu);

                chunk.m_bitLength              = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
                chunk.m_data                   = msg.m_data;
                chunk.m_fragmentGroup          = group;
                chunk.m_fragmentGroupTotalBits = totalBytes * 8;
                chunk.m_fragmentChunkByteSize  = bytesPerChunk;
                chunk.m_fragmentChunkNumber    = i;

                NetException.Assert(chunk.m_bitLength != 0);
                NetException.Assert(chunk.GetEncodedSize() < mtu);

                Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);

                foreach (NetConnection recipient in recipients)
                {
                    recipient.EnqueueMessage(chunk, method, sequenceChannel);
                }

                bitsLeft -= bitsPerChunk;
            }

            return;
        }
Example #2
0
        internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
        {
            //  8 bits - NetMessageType
            //  1 bit  - Fragment?
            // 15 bits - Sequence number
            // 16 bits - Payload length in bits

            intoBuffer[ptr++] = (byte)m_messageType;

            byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));

            intoBuffer[ptr++] = low;
            intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);

            if (m_fragmentGroup == 0)
            {
                intoBuffer[ptr++] = (byte)m_bitLength;
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);

                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }
            else
            {
                int wasPtr = ptr;
                intoBuffer[ptr++] = (byte)m_bitLength;
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);

                //
                // write fragmentation header
                //
                ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
                int hdrLen = ptr - wasPtr - 2;

                // update length
                int realBitLength = m_bitLength + (hdrLen * 8);
                intoBuffer[wasPtr]     = (byte)realBitLength;
                intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);

                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }

            NetException.Assert(ptr > 0);
            return(ptr);
        }
Example #3
0
        internal int GetEncodedSize()
        {
            int retval = NetConstants.UnfragmentedMessageHeaderSize; // regular headers

            if (m_fragmentGroup != 0)
            {
                retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
            }
            retval += this.LengthBytes;

            return(retval);
        }
        private void HandleReleasedFragment(NetIncomingMessage im)
        {
            //
            // read fragmentation header and combine fragments
            //
            int group;
            int totalBits;
            int chunkByteSize;
            int chunkNumber;
            int ptr = NetFragmentationHelper.ReadHeader(
                im.m_data, 0,
                out group,
                out totalBits,
                out chunkByteSize,
                out chunkNumber
                );

            NetException.Assert(im.LengthBytes > ptr);

            NetException.Assert(group > 0);
            NetException.Assert(totalBits > 0);
            NetException.Assert(chunkByteSize > 0);

            int totalBytes     = NetUtility.BytesToHoldBits((int)totalBits);
            int totalNumChunks = totalBytes / chunkByteSize;

            if (totalNumChunks * chunkByteSize < totalBytes)
            {
                totalNumChunks++;
            }

            NetException.Assert(chunkNumber < totalNumChunks);

            if (chunkNumber >= totalNumChunks)
            {
                LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
                return;
            }

            Dictionary <int, ReceivedFragmentGroup> groups;

            if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
            {
                groups = new Dictionary <int, ReceivedFragmentGroup>();
                m_receivedFragmentGroups[im.SenderConnection] = groups;
            }

            ReceivedFragmentGroup info;

            if (!groups.TryGetValue(group, out info))
            {
                info                = new ReceivedFragmentGroup();
                info.Data           = new byte[totalBytes];
                info.ReceivedChunks = new NetBitVector(totalNumChunks);
                groups[group]       = info;
            }

            info.ReceivedChunks[chunkNumber] = true;
            info.LastReceived = (float)NetTime.Now;

            // copy to data
            int offset = (chunkNumber * chunkByteSize);

            Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);

            int cnt = info.ReceivedChunks.Count();

            //LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");

            LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");

            if (info.ReceivedChunks.Count() == totalNumChunks)
            {
                // Done! Transform this incoming message
                im.m_data       = info.Data;
                im.m_bitLength  = (int)totalBits;
                im.m_isFragment = false;

                LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");

                ReleaseMessage(im);
            }
            else
            {
                // data has been copied; recycle this incoming message
                Recycle(im);
            }

            return;
        }