using System; using System.Collections.Generic; using System.Threading; using System.Net; namespace Lidgren.Network { public partial class NetPeer { /// /// Send a message to a specific connection /// /// The message to send /// The recipient connection /// How to deliver the message public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method) { return SendMessage(msg, recipient, method, 0); } /// /// Send a message to a specific connection /// /// The message to send /// The recipient connection /// How to deliver the message /// Sequence channel within the delivery method 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); } } internal static int GetMTU(IList recipients) { int count = recipients.Count; int mtu = int.MaxValue; if (count < 1) { #if DEBUG throw new NetException("GetMTU called with no recipients"); #else // we don't have access to the particular peer, so just use default MTU return NetPeerConfiguration.kDefaultMTU; #endif } for(int i=0;i /// Send a message to a list of connections /// /// The message to send /// The list of recipients to send to /// How to deliver the message /// Sequence channel within the delivery method public void SendMessage(NetOutgoingMessage msg, List 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; } /// /// Send a message to an unconnected host /// public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port) { if (msg == null) throw new ArgumentNullException("msg"); if (host == null) throw new ArgumentNullException("host"); if (msg.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit) throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")"); IPAddress adr = NetUtility.Resolve(host); if (adr == null) throw new NetException("Failed to resolve " + host); msg.m_messageType = NetMessageType.Unconnected; msg.m_isSent = true; Interlocked.Increment(ref msg.m_recyclingCount); m_unsentUnconnectedMessages.Enqueue(new NetTuple(new IPEndPoint(adr, port), msg)); } /// /// Send a message to an unconnected host /// public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient) { if (msg == null) throw new ArgumentNullException("msg"); if (recipient == null) throw new ArgumentNullException("recipient"); if (msg.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit) throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")"); msg.m_messageType = NetMessageType.Unconnected; msg.m_isSent = true; Interlocked.Increment(ref msg.m_recyclingCount); m_unsentUnconnectedMessages.Enqueue(new NetTuple(recipient, msg)); } /// /// Send a message to an unconnected host /// public void SendUnconnectedMessage(NetOutgoingMessage msg, IList recipients) { 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 (msg.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit) throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")"); msg.m_messageType = NetMessageType.Unconnected; msg.m_isSent = true; Interlocked.Add(ref msg.m_recyclingCount, recipients.Count); foreach(IPEndPoint ep in recipients) m_unsentUnconnectedMessages.Enqueue(new NetTuple(ep, msg)); } /// /// Send a message to this exact same netpeer (loopback) /// public void SendUnconnectedToSelf(NetOutgoingMessage msg) { if (msg == null) throw new ArgumentNullException("msg"); if (msg.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); msg.m_messageType = NetMessageType.Unconnected; msg.m_isSent = true; if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false) return; // dropping unconnected message since it's not enabled for receiving NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes); om.Write(msg); om.m_isFragment = false; om.m_receiveTime = NetTime.Now; om.m_senderConnection = null; om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint; NetException.Assert(om.m_bitLength == msg.LengthBits); ReleaseMessage(om); } } }