// 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); }
// 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; }
/// <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)); } }
// 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); } } }
// 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); }
// 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)); }
// 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); }
// 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; }
/// <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); }
/// <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; } }
// 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); }
/// <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; }