private EnqueueMessage ( |
||
msg | ||
method | NetDeliveryMethod | |
sequenceChannel | int | |
return | NetSendResult |
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">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 msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) throw new ArgumentNullException("msg"); if (recipient == null) throw new ArgumentNullException("recipient"); NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); if (msg.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); int len = msg.LengthBytes; if (len <= m_configuration.MaximumTransmissionUnit) { Interlocked.Increment(ref msg.m_recyclingCount); return recipient.EnqueueMessage(msg, method, sequenceChannel); } else { // message must be fragmented! SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return NetSendResult.Queued; // could be different for each connection; Queued is "most true" } }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">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 msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) throw new ArgumentNullException("msg"); if (recipient == null) throw new ArgumentNullException("recipient"); if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod) throw new ArgumentOutOfRangeException("sequenceChannel"); NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); 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 len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize if (len <= recipient.m_currentMTU) { Interlocked.Increment(ref msg.m_recyclingCount); return recipient.EnqueueMessage(msg, method, sequenceChannel); } else { // message must be fragmented! SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return NetSendResult.Queued; // could be different for each connection; Queued is "most true" } }
/// <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); int ct = recipients.Count; for (int i = 0; i < ct; ++i) { NetConnection conn = recipients[i]; 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; }
/// <summary> /// Sends message to server /// </summary> public NetSendResult Send(NetOutgoingMessage msg, NetDeliveryMethod method = NetDeliveryMethod.ReliableOrdered, int sequenceChannel = 0) { NetConnection serverConnection = ServerConnection; if (serverConnection == null) { LogWarning("Cannot send message, no server connection!"); Recycle(msg); return(NetSendResult.FailedNotConnected); } if (msg == null) { throw new ArgumentNullException("msg"); } if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod) { throw new ArgumentOutOfRangeException("sequenceChannel"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); 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; Network.Statistics.UploadedBytes += (uint)msg.LengthBytes; Network.Statistics.uploadBytesPerSecLast += (uint)msg.LengthBytes; bool suppressFragmentation = (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.UnreliableSequenced) && m_configuration.UnreliableSizeBehaviour != NetUnreliableSizeBehaviour.NormalFragmentation; int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize if (len <= serverConnection.m_currentMTU || suppressFragmentation) { Interlocked.Increment(ref msg.m_recyclingCount); return(serverConnection.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! if (serverConnection.m_status != NetConnectionStatus.Connected) { return(NetSendResult.FailedNotConnected); } return(SendFragmentedMessage(msg, new NetConnection[] { serverConnection }, method, sequenceChannel)); } }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">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 msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) { throw new ArgumentNullException("msg"); } if (recipient == null) { throw new ArgumentNullException("recipient"); } if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod) { throw new ArgumentOutOfRangeException("sequenceChannel"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); 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; bool suppressFragmentation = (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.UnreliableSequenced) && m_configuration.UnreliableSizeBehaviour != NetUnreliableSizeBehaviour.NormalFragmentation; int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize if (len <= recipient.m_currentMTU || suppressFragmentation) { Interlocked.Increment(ref msg.m_recyclingCount); return(recipient.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! if (recipient.m_status != NetConnectionStatus.Connected) { return(NetSendResult.FailedNotConnected); } // return SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); var tmp = ConnectionListPool.Rent(); tmp.Add(recipient); var result = SendFragmentedMessage(msg, tmp, method, sequenceChannel); ConnectionListPool.Return(tmp); return(result); } }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">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 msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) { throw new ArgumentNullException("msg"); } if (recipient == null) { throw new ArgumentNullException("recipient"); } if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod) { throw new ArgumentOutOfRangeException("sequenceChannel"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); 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 len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize if (len <= recipient.m_currentMTU) { Interlocked.Increment(ref msg.m_recyclingCount); return(recipient.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! if (recipient.m_status != NetConnectionStatus.Connected) { return(NetSendResult.FailedNotConnected); } SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return(NetSendResult.Queued); // could be different for each connection; Queued is "most true" } }
/// <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); } } }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">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 msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) { throw new ArgumentNullException("msg"); } if (recipient == null) { throw new ArgumentNullException("recipient"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); if (msg.m_isSent) { throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); } int len = msg.LengthBytes; if (len <= m_configuration.MaximumTransmissionUnit) { Interlocked.Increment(ref msg.m_recyclingCount); return(recipient.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return(NetSendResult.Queued); // could be different for each connection; Queued is "most true" } }
// 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 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); int ct = recipients.Count; for (int ix = 0; ix < ct; ++ix) { NetConnection recipient = recipients[ix]; recipient.EnqueueMessage(chunk, method, sequenceChannel); } bitsLeft -= bitsPerChunk; } return; }