// 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;
        }
Esempio n. 2
0
        internal int Encode(Span <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)
                {
                    m_data.Slice(0, byteLen)
                    .CopyTo(intoBuffer.Slice(ptr, byteLen));
                    //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)
                {
                    m_data.Slice(m_fragmentChunkNumber * m_fragmentChunkByteSize, byteLen)
                    .CopyTo(intoBuffer.Slice(ptr, byteLen));
                    //Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }

            NetException.Assert(ptr > 0);
            return(ptr);
        }
Esempio n. 3
0
        // on user thread
        private void SendFragmentedMessage(NetOutgoingMessage msg, IList <NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
        {
            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;
            int mtu        = m_configuration.MaximumTransmissionUnit;

            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;
        }
        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 += LengthBytes;
            return(retval);
        }
        private void HandleReleasedFragment(NetIncomingMessage im)
        {
            VerifyNetworkThread();

            //
            // 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)");
                groups.Remove(group);

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

            return;
        }
Esempio n. 6
0
        internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
        {
            // NOTE: I had to change the header format so it won't collide with multiplexed STUN on the same socket
            //       and I made it big-endian for consistency's sake. -AR
            //
            //  <- MSB                                                     LSB ->
            //   0                   1                   2                   3
            //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            //  V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V
            //  |1|F| Sequence Number           |NetMessageType |  Length bits...
            //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            //  ...             |
            //  +-+-+-+-+-+-+-+-+
            //
            // IMPORTANT: The packet format is also encoded elsewhere, to send NetMessageType.Acknowledge (see NetConnection.cs)
            //

            intoBuffer[ptr++] = (byte)(((sequenceNumber >> 8) & 0x3F) | (m_fragmentGroup == 0 ? 0x80 : 0xC0));
            intoBuffer[ptr++] = (byte)sequenceNumber;

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

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


                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 >> 8);
                intoBuffer[ptr++] = (byte)m_bitLength;


                //
                // 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 >> 8);
                intoBuffer[wasPtr + 1] = (byte)realBitLength;

                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);
        }
        // on user thread
        // the message must not be sent already
        private NetSendResult SendFragmentedMessage(
            NetOutgoingMessage message,
            IEnumerable <NetConnection?> recipients,
            NetDeliveryMethod method,
            int sequenceChannel)
        {
            // determine minimum mtu for all recipients
            int mtu = GetMTU(recipients, out int recipientCount);

            if (recipientCount == 0)
            {
                Recycle(message);
                return(NetSendResult.NoRecipients);
            }

            int group = GetNextFragmentGroup();

            // do not send msg; but set fragmentgroup in case user tries to recycle it immediately
            message._fragmentGroup = group << 2 | 1;

            // create fragmentation specifics
            int totalBytes = message.ByteLength;

            int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);

            int numChunks = totalBytes / bytesPerChunk;

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

            var retval = NetSendResult.Sent;

            int bitsPerChunk = bytesPerChunk * 8;
            int bitsLeft     = message.BitLength;

            byte[] buffer = message.GetBuffer();

            for (int i = 0; i < numChunks; i++)
            {
                NetOutgoingMessage chunk = CreateMessage();
                chunk.SetBuffer(buffer, false);
                chunk.BitLength = Math.Min(bitsLeft, bitsPerChunk);

                chunk._fragmentGroup          = group << 2 | 1;
                chunk._fragmentGroupTotalBits = totalBytes * 8;
                chunk._fragmentChunkByteSize  = bytesPerChunk;
                chunk._fragmentChunkNumber    = i;

                LidgrenException.Assert(chunk.BitLength != 0);
                LidgrenException.Assert(chunk.GetEncodedSize() <= mtu);

                Interlocked.Add(ref chunk._recyclingCount, recipientCount);

                foreach (NetConnection?recipient in recipients.AsListEnumerator())
                {
                    if (recipient == null)
                    {
                        continue;
                    }

                    NetSendResult result = recipient.EnqueueMessage(chunk, method, sequenceChannel).Result;
                    if (result > retval)
                    {
                        retval = result; // return "worst" result
                    }
                }

                bitsLeft -= bitsPerChunk;
            }
            return(retval);
        }
        // on user thread
        private async ValueTask <NetSendResult> SendFragmentedMessageAsync(
            PipeReader reader,
            NetConnection recipient,
            int sequenceChannel,
            CancellationToken cancellationToken)
        {
            int group = GetNextFragmentGroup();

            (NetSendResult Result, NetSenderChannel?) SendChunk(NetOutgoingMessage chunk)
            {
                return(recipient.EnqueueMessage(chunk, NetDeliveryMethod.ReliableOrdered, sequenceChannel));
            }

            NetSendResult finalResult;
            Exception?    exception = null;

            try
            {
                ReadResult readResult;
                do
                {
                    readResult = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                    if (readResult.IsCanceled)
                    {
                        NetOutgoingMessage cancelChunk = CreateStreamChunk(0, group, NetStreamFragmentType.Cancelled);
                        finalResult = SendChunk(cancelChunk).Result;
                        break;
                    }

                    ReadOnlySequence <byte> buffer = readResult.Buffer;

                    int mtu           = recipient.CurrentMTU;
                    int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, (int)buffer.Length, mtu);

                    while (buffer.Length > 0)
                    {
                        long chunkLength         = Math.Min(buffer.Length, bytesPerChunk);
                        NetOutgoingMessage chunk = CreateStreamChunk((int)chunkLength, group, NetStreamFragmentType.Data);
                        foreach (ReadOnlyMemory <byte> memory in buffer.Slice(0, chunkLength))
                        {
                            chunk.Write(memory.Span);
                        }
                        buffer = buffer.Slice(chunkLength);

                        LidgrenException.Assert(chunk.GetEncodedSize() <= mtu);
                        Interlocked.Add(ref chunk._recyclingCount, 1);

                        var(result, channel) = SendChunk(chunk);
                        if (result == NetSendResult.Queued)
                        {
                            await channel !.WaitForIdleAsync(millisecondsTimeout: 10000, cancellationToken);
                        }
                        else if (result != NetSendResult.Sent)
                        {
                            NetOutgoingMessage cancelChunk = CreateStreamChunk(0, group, NetStreamFragmentType.Cancelled);
                            finalResult = SendChunk(cancelChunk).Result;
                            reader.CancelPendingRead();
                            break;
                        }
                    }

                    reader.AdvanceTo(readResult.Buffer.End);
                }while (!readResult.IsCompleted);

                NetOutgoingMessage endChunk = CreateStreamChunk(0, group, NetStreamFragmentType.EndOfStream);
                finalResult = SendChunk(endChunk).Result;
            }
            catch (Exception ex)
            {
                exception = ex;

                NetOutgoingMessage errorChunk = CreateStreamChunk(0, group, NetStreamFragmentType.ServerError);
                finalResult = SendChunk(errorChunk).Result;
            }

            await reader.CompleteAsync(exception).ConfigureAwait(false);

            return(finalResult);
        }