// 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 ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.UnreliableSequenced) && msg.GetEncodedSize() > m_currentMTU) m_peer.ThrowOrLog("Reliable 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!"); 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); }
/// <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) { 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.Dropped) Interlocked.Decrement(ref msg.m_recyclingCount); } } else { // message must be fragmented! SendFragmentedMessage(msg, recipients, method, sequenceChannel); } return; }