Пример #1
0
        // called by SendMessage() and NetPeer.SendMessage; ie. may be user thread
        internal NetSendResult EnqueueMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
        {
            if (m_status != NetConnectionStatus.Connected)
            {
                return(NetSendResult.FailedNotConnected);
            }

            NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);

            msg.m_messageType = tp;

            // TODO: do we need to make this more thread safe?
            int channelSlot           = (int)method - 1 + sequenceChannel;
            NetSenderChannelBase chan = m_sendChannels[channelSlot];

            if (chan == null)
            {
                chan = CreateSenderChannel(tp);
            }

            if (msg.GetEncodedSize() > m_currentMTU)
            {
                throw new NetException("Message too large! Fragmentation failure?");
            }

            var retval = chan.Enqueue(msg);

            if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
            {
                retval = NetSendResult.Queued;                 // queued since we're not autoflushing
            }
            return(retval);
        }
Пример #2
0
        // Queue an item for immediate sending on the wire
        // This method is called from the ISenderChannels
        internal void QueueSendMessage(NetOutgoingMessage om, int seqNr)
        {
            m_peer.VerifyNetworkThread();

            int sz = om.GetEncodedSize();

            if (sz > m_currentMTU)
            {
                m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");
            }

            if (m_sendBufferWritePtr + sz > m_currentMTU)
            {
                bool connReset;                                                               // TODO: handle connection reset
                NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); // or else the message should have been fragmented earlier
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndpoint, m_sendBufferNumMessages, out connReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr    = 0;
                m_sendBufferNumMessages = 0;
            }

            m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
            m_sendBufferNumMessages++;

            NetException.Assert(m_sendBufferWritePtr > 0, "Encoded zero size message?");
            NetException.Assert(m_sendBufferNumMessages > 0);
        }
        // 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;
        }
Пример #4
0
        /// <summary>
        /// Send a message to a list of connections
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="recipients">The list of recipients to send to</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        /// <param name="except">Connection where message should not be sent, optional</param>
        public void SendMessage(NetOutgoingMessage msg, List <NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel, NetConnection except = null)
        {
            if (msg == null)
            {
                throw new ArgumentNullException("msg");
            }
            if (recipients == null)
            {
                throw new ArgumentNullException("recipients");
            }
            if (recipients.Count < 1)
            {
                throw new NetException("recipients must contain at least one item");
            }
            if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
            {
                NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
            }
            if (msg.m_isSent)
            {
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            }

            int mtu = GetMTU(recipients);

            msg.m_isSent = true;

            int len = msg.GetEncodedSize();

            if (len <= mtu)
            {
                Interlocked.Add(ref msg.m_recyclingCount, except != null ? recipients.Count - 1 : recipients.Count);
                foreach (NetConnection conn in recipients)
                {
                    if (except != null && conn.RemoteUniqueIdentifier == except.RemoteUniqueIdentifier)
                    {
                        continue;
                    }

                    if (conn == null)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                        continue;
                    }
                    NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
                    if (res != NetSendResult.Queued && res != NetSendResult.Sent)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                    }
                }
            }
            else
            {
                // message must be fragmented!
                SendFragmentedMessage(msg, recipients, method, sequenceChannel, except);
            }

            return;
        }
        /// <summary>
        /// Send a message to a list of connections.
        /// </summary>
        /// <param name="message">The message to send</param>
        /// <param name="recipients">The list of recipients to send to</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public NetSendResult SendMessage(
            NetOutgoingMessage message,
            IEnumerable <NetConnection?> recipients,
            NetDeliveryMethod method,
            int sequenceChannel)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            if (recipients == null)
            {
                throw new ArgumentNullException(nameof(recipients));
            }

            NetConstants.AssertValidDeliveryChannel(
                method, sequenceChannel, nameof(method), nameof(sequenceChannel));

            message.AssertNotSent(nameof(message));
            message._isSent = true;

            int mtu = GetMTU(recipients, out int recipientCount);

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

            int length = message.GetEncodedSize();

            if (length <= mtu)
            {
                Interlocked.Add(ref message._recyclingCount, recipientCount);

                var retval = NetSendResult.Sent;
                foreach (NetConnection?conn in recipients.AsListEnumerator())
                {
                    if (conn == null)
                    {
                        continue;
                    }

                    NetSendResult result = conn.EnqueueMessage(message, method, sequenceChannel).Result;
                    if (result > retval)
                    {
                        retval = result; // return "worst" result
                    }
                }
                return(retval);
            }
            else
            {
                // message must be fragmented!
                return(SendFragmentedMessage(message, recipients, method, sequenceChannel));
            }
        }
Пример #6
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;
        }
        /// <summary>
        /// Send a message to a specific connection.
        /// </summary>
        /// <param name="message">The message to send</param>
        /// <param name="recipient">The recipient connection</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public NetSendResult SendMessage(
            NetOutgoingMessage message, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            if (recipient == null)
            {
                throw new ArgumentNullException(nameof(recipient));
            }

            NetConstants.AssertValidDeliveryChannel(
                method, sequenceChannel, nameof(method), nameof(sequenceChannel));

            message.AssertNotSent(nameof(message));
            message._isSent = true;

            bool suppressFragmentation =
                (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.UnreliableSequenced) &&
                Configuration.UnreliableSizeBehaviour != NetUnreliableSizeBehaviour.NormalFragmentation;

            if (suppressFragmentation || message.GetEncodedSize() <= recipient.CurrentMTU)
            {
                Interlocked.Increment(ref message._recyclingCount);
                return(recipient.EnqueueMessage(message, method, sequenceChannel).Result);
            }
            else
            {
                // message must be fragmented!
                if (recipient.Status != NetConnectionStatus.Connected)
                {
                    return(NetSendResult.FailedNotConnected);
                }

                List <NetConnection> recipients = NetConnectionListPool.Rent(1);
                try
                {
                    recipients.Add(recipient);
                    return(SendFragmentedMessage(message, recipients, method, sequenceChannel));
                }
                finally
                {
                    NetConnectionListPool.Return(recipients);
                }
            }
        }
Пример #8
0
        // Queue an item for immediate sending on the wire
        // This method is called from the ISenderChannels
        internal void QueueSendMessage(NetOutgoingMessage om, int seqNr)
        {
            m_peer.VerifyNetworkThread();

            int sz = om.GetEncodedSize();
            //if (sz > m_currentMTU)
            //	m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");

            bool connReset;             // TODO: handle connection reset

            // can fit this message together with previously written to buffer?
            if (m_sendBufferWritePtr + sz > m_currentMTU)
            {
                if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0)
                {
                    // previous message in buffer; send these first
                    m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                    m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                    m_sendBufferWritePtr    = 0;
                    m_sendBufferNumMessages = 0;
                }
            }

            // encode it into buffer regardless if it (now) fits within MTU or not
            m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
            m_sendBufferNumMessages++;

            if (m_sendBufferWritePtr > m_currentMTU)
            {
                // send immediately; we're already over MTU
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr    = 0;
                m_sendBufferNumMessages = 0;
            }

            if (m_sendBufferWritePtr > 0)
            {
                m_peer.m_needFlushSendQueue = true;                 // flush in heartbeat
            }
            Interlocked.Decrement(ref om.m_recyclingCount);
        }
Пример #9
0
        // called by SendMessage() and NetPeer.SendMessage; ie. may be user thread
        internal NetSendResult EnqueueMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
        {
            NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);

            msg.m_messageType = tp;

            // TODO: do we need to make this more thread safe?
            int channelSlot           = (int)method - 1 + sequenceChannel;
            NetSenderChannelBase chan = m_sendChannels[channelSlot];

            if (chan == null)
            {
                chan = CreateSenderChannel(tp);
            }

            if (msg.GetEncodedSize() > m_currentMTU)
            {
                throw new NetException("Message too large! Fragmentation failure?");
            }

            return(chan.Enqueue(msg));
        }
Пример #10
0
		// Queue an item for immediate sending on the wire
		// This method is called from the ISenderChannels
		internal void QueueSendMessage(NetOutgoingMessage om, int seqNr)
		{
			m_peer.VerifyNetworkThread();

			int sz = om.GetEncodedSize();
			if (sz > m_currentMTU)
				m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");

			if (m_sendBufferWritePtr + sz > m_currentMTU)
			{
				bool connReset; // TODO: handle connection reset
				NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); // or else the message should have been fragmented earlier
				m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
				m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
				m_sendBufferWritePtr = 0;
				m_sendBufferNumMessages = 0;
			}

			m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
			m_sendBufferNumMessages++;

			NetException.Assert(m_sendBufferWritePtr > 0, "Encoded zero size message?");
			NetException.Assert(m_sendBufferNumMessages > 0);
		}
Пример #11
0
		// called by SendMessage() and NetPeer.SendMessage; ie. may be user thread
		internal NetSendResult EnqueueMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
		{
			if (m_status != NetConnectionStatus.Connected)
				return NetSendResult.FailedNotConnected;

			NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);
			msg.m_messageType = tp;

			// TODO: do we need to make this more thread safe?
			int channelSlot = (int)method - 1 + sequenceChannel;
			NetSenderChannelBase chan = m_sendChannels[channelSlot];
			if (chan == null)
				chan = CreateSenderChannel(tp);

			if (msg.GetEncodedSize() > m_currentMTU)
				throw new NetException("Message too large! Fragmentation failure?");

			var retval = chan.Enqueue(msg);
			if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
				retval = NetSendResult.Queued; // queued since we're not autoflushing
			return retval;
		}
Пример #12
0
		/// <summary>
		/// Send a message to a list of connections
		/// </summary>
		/// <param name="msg">The message to send</param>
		/// <param name="recipients">The list of recipients to send to</param>
		/// <param name="method">How to deliver the message</param>
		/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
		public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
		{
			if (msg == null)
				throw new ArgumentNullException("msg");
			if (recipients == null)
				throw new ArgumentNullException("recipients");
			if (recipients.Count < 1)
				throw new NetException("recipients must contain at least one item");
			if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
				NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
			if (msg.m_isSent)
				throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");

			int mtu = GetMTU(recipients);

			msg.m_isSent = true;

			int len = msg.GetEncodedSize();
			if (len <= mtu)
			{
				Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
				foreach (NetConnection conn in recipients)
				{
					if (conn == null)
					{
						Interlocked.Decrement(ref msg.m_recyclingCount);
						continue;
					}
					NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
					if (res != NetSendResult.Queued && res != NetSendResult.Sent)
						Interlocked.Decrement(ref msg.m_recyclingCount);
				}
			}
			else
			{
				// message must be fragmented!
				SendFragmentedMessage(msg, recipients, method, sequenceChannel);
			}

			return;
		}
        // 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);
        }
        // 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);
        }
Пример #15
0
        /// <summary>
        /// Send a message to a list of connections
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="recipients">The list of recipients to send to</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public void SendMessage(NetOutgoingMessage msg, IList <NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
        {
            if (msg == null)
            {
                throw new ArgumentNullException("msg");
            }
            if (recipients == null)
            {
                if (msg.m_isSent == false)
                {
                    Recycle(msg);
                }
                throw new ArgumentNullException("recipients");
            }
            if (recipients.Count < 1)
            {
                if (msg.m_isSent == false)
                {
                    Recycle(msg);
                }
                throw new NetException("recipients must contain at least one item");
            }
            if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
            {
                NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
            }
            if (msg.m_isSent)
            {
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            }
            msg.m_isSent = true;

            int mtu = GetMTU(recipients);

            int len = msg.GetEncodedSize();

            if (len <= mtu)
            {
                var recipientCount = recipients.Count;

                Interlocked.Add(ref msg.m_recyclingCount, recipientCount);

                for (var i = 0; i < recipientCount; i++)
                {
                    var conn = recipients[i];
                    if (conn == null)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                        continue;
                    }
                    NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
                    if (res == NetSendResult.Dropped)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                    }
                }
            }
            else
            {
                // message must be fragmented!
                SendFragmentedMessage(msg, recipients, method, sequenceChannel);
            }

            return;
        }
        // Queue an item for immediate sending on the wire
        // This method is called from the ISenderChannels
        internal void QueueSendMessage(NetOutgoingMessage om, int seqNr)
        {
            m_peer.VerifyNetworkThread();

            int sz = om.GetEncodedSize();
            //if (sz > m_currentMTU)
            //	m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");

            bool connReset; // TODO: handle connection reset

            // can fit this message together with previously written to buffer?
            if (m_sendBufferWritePtr + sz > m_currentMTU)
            {
                if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0)
                {
                    // previous message in buffer; send these first
                    m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                    m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                    m_sendBufferWritePtr = 0;
                    m_sendBufferNumMessages = 0;
                }
            }

            // encode it into buffer regardless if it (now) fits within MTU or not
            m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
            m_sendBufferNumMessages++;

            if (m_sendBufferWritePtr > m_currentMTU)
            {
                // send immediately; we're already over MTU
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr = 0;
                m_sendBufferNumMessages = 0;
            }
        }
Пример #17
0
		// called by SendMessage() and NetPeer.SendMessage; ie. may be user thread
		internal NetSendResult EnqueueMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
		{
			NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);
			msg.m_messageType = tp;

			// TODO: do we need to make this more thread safe?
			int channelSlot = (int)method - 1 + sequenceChannel;
			NetSenderChannelBase chan = m_sendChannels[channelSlot];
			if (chan == null)
				chan = CreateSenderChannel(tp);

			if (msg.GetEncodedSize() > m_currentMTU)
				throw new NetException("Message too large! Fragmentation failure?");

			return chan.Enqueue(msg);
		}
Пример #18
0
        /// <summary>
        /// Send a message to a list of connections
        /// </summary>
        /// <param name="msg">The message to send</param>
        /// <param name="recipients">The list of recipients to send to</param>
        /// <param name="method">How to deliver the message</param>
        /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
        public void SendTo(NetOutgoingMessage msg, IList <NetConnection> recipients, NetDeliveryMethod method = NetDeliveryMethod.ReliableOrdered, int sequenceChannel = 0)
        {
            if (msg == null)
            {
                throw new ArgumentNullException("msg");
            }
            if (recipients == null)
            {
                if (msg.m_isSent == false)
                {
                    Recycle(msg);
                }
                throw new ArgumentNullException("recipients");
            }
            if (recipients.Count < 1)
            {
                if (msg.m_isSent == false)
                {
                    Recycle(msg);
                }
                //throw new NetException("recipients must contain at least one item");
                return;
            }
            if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
            {
                NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
            }
            if (msg.m_isSent)
            {
                throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
            }
            msg.m_isSent = true;

            int mtu = GetMTU(recipients);

            int len = msg.GetEncodedSize();

            if (len <= mtu)
            {
                Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
                foreach (var conn in recipients)
                {
                    if (conn == null)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                        continue;
                    }
                    if (conn.m_isThrottled)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                        conn.m_throttledMessages.Add(new NetConnection.ThrottledMessage(msg, method, sequenceChannel));
                        continue;
                    }

                    Network.Statistics.UploadedBytes         += (uint)msg.LengthBytes;
                    Network.Statistics.uploadBytesPerSecLast += (uint)msg.LengthBytes;

                    NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
                    if (res == NetSendResult.Dropped)
                    {
                        Interlocked.Decrement(ref msg.m_recyclingCount);
                    }
                }
            }
            else
            {
                Network.Statistics.UploadedBytes         += (uint)msg.LengthBytes;
                Network.Statistics.uploadBytesPerSecLast += (uint)msg.LengthBytes;

                // message must be fragmented!
                SendFragmentedMessage(msg, recipients, method, sequenceChannel);
            }
            return;
        }