internal override NetSendResult Enqueue(NetOutgoingMessage message) { m_queuedSends.Enqueue(message); m_connection.m_peer.m_needFlushSendQueue = true; // a race condition to this variable will simply result in a single superflous call to FlushSendQueue() if (m_queuedSends.Count <= GetAllowedSends()) return NetSendResult.Sent; return NetSendResult.Queued; }
/// <summary> /// Encrypt an outgoing message /// </summary> public override bool Encrypt(NetOutgoingMessage msg) { int numBytes = msg.LengthBytes; for (int i = 0; i < numBytes; i++) { int offset = i % m_key.Length; msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]); } return true; }
/// <summary> /// Send a message to all connections /// </summary> /// <param name="msg">The message to send</param> /// <param name="method">How to deliver the message</param> public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method) { var all = this.Connections; if (all.Count <= 0) { if (msg.m_isSent == false) Recycle(msg); return; } SendMessage(msg, all, method, 0); }
/// <summary> /// Approves this connection; sending a connection response to the remote host /// </summary> /// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param> public void Approve(NetOutgoingMessage localHail) { if (m_status != NetConnectionStatus.RespondedAwaitingApproval) { m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status); return; } m_localHailMessage = localHail; m_handshakeAttempts = 0; SendConnectResponse(NetTime.Now, false); }
/// <summary> /// Creates a new message for sending /// </summary> /// <param name="initialCapacity">initial capacity in bytes</param> public NetOutgoingMessage CreateMessage(int initialCapacity) { NetOutgoingMessage retval; if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval)) retval = new NetOutgoingMessage(); NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount); if (initialCapacity > 0) retval.m_data = GetStorage(initialCapacity); 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) { 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; }
/// <summary> /// Send a discovery response message /// </summary> public void SendDiscoveryResponse(NetOutgoingMessage msg, NetEndPoint recipient) { if (recipient == null) throw new ArgumentNullException("recipient"); if (msg == null) msg = CreateMessage(0); else if (msg.m_isSent) throw new NetException("Message has already been sent!"); if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit) throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)"); msg.m_messageType = NetMessageType.DiscoveryResponse; Interlocked.Increment(ref msg.m_recyclingCount); m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(recipient, msg)); }
public override bool Encrypt(NetOutgoingMessage msg) { int unEncLenBits = msg.LengthBits; var ms = new MemoryStream(); var cs = GetEncryptStream(ms); cs.Write(msg.m_data, 0, msg.LengthBytes); cs.Close(); // get results var arr = ms.ToArray(); ms.Close(); msg.EnsureBufferSize((arr.Length + 4) * 8); msg.LengthBits = 0; // reset write pointer msg.Write((uint)unEncLenBits); msg.Write(arr); msg.LengthBits = (arr.Length + 4) * 8; return true; }
/// <summary> /// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted /// </summary> public override bool Encrypt(NetOutgoingMessage msg) { int payloadBitLength = msg.LengthBits; int numBytes = msg.LengthBytes; int blockSize = BlockSize; int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize); int dstSize = numBlocks * blockSize; msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written for(int i=0;i<numBlocks;i++) { EncryptBlock(msg.m_data, (i * blockSize), m_tmp); Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length); } // add true payload length last msg.Write((UInt32)payloadBitLength); return true; }
/// <summary> /// Send a message to all connections except one /// </summary> /// <param name="msg">The message to send</param> /// <param name="method">How to deliver the message</param> /// <param name="except">Don't send to this particular connection</param> /// <param name="sequenceChannel">Which sequence channel to use for the message</param> public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel) { var all = this.Connections; if (all.Count <= 0) { if (msg.m_isSent == false) Recycle(msg); return; } if (except == null) { SendMessage(msg, all, method, sequenceChannel); return; } List<NetConnection> recipients = new List<NetConnection>(all.Count - 1); foreach (var conn in all) if (conn != except) recipients.Add(conn); if (recipients.Count > 0) SendMessage(msg, recipients, 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); } }
/// <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> public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method) { return SendMessage(msg, recipient, method, 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); }
/// <summary> /// Connect to a remote server /// </summary> /// <param name="remoteEndPoint">The remote endpoint to connect to</param> /// <param name="hailMessage">The hail message to pass</param> /// <returns>server connection, or null if already connected</returns> public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) { lock (m_connections) { if (m_connections.Count > 0) { LogWarning("Connect attempt failed; Already connected"); return null; } } lock (m_handshakes) { if (m_handshakes.Count > 0) { LogWarning("Connect attempt failed; Handshake already in progress"); return null; } } return base.Connect(remoteEndPoint, hailMessage); }
/// <summary> /// Send a message to this remote connection /// </summary> /// <param name="msg">The message to send</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, NetDeliveryMethod method, int sequenceChannel) { return m_peer.SendMessage(msg, this, method, sequenceChannel); }
// 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; }
public void Reset() { NumSent = 0; LastSent = 0.0; Message = null; }
/// <summary> /// Encrypt an outgoing message in place /// </summary> public abstract bool Encrypt(NetOutgoingMessage msg);
internal void Recycle(NetOutgoingMessage msg) { if (m_outgoingMessagesPool == null) return; #if DEBUG NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled outgoing message! Thread race?"); if (msg.m_recyclingCount != 0) LogWarning("Wrong recycling count! should be zero; found " + msg.m_recyclingCount); #endif // setting m_recyclingCount to zero SHOULD be an unnecessary maneuver, if it's not zero something is wrong // however, in RELEASE, we'll just have to accept this and move on with life msg.m_recyclingCount = 0; byte[] storage = msg.m_data; msg.m_data = null; // message fragments cannot be recycled // TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them if (msg.m_fragmentGroup == 0) Recycle(storage); msg.Reset(); if (m_outgoingMessagesPool.Count < m_maxCacheCount) m_outgoingMessagesPool.Enqueue(msg); }
/// <summary> /// Sends message to server /// </summary> public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel) { NetConnection serverConnection = ServerConnection; if (serverConnection == null) { LogWarning("Cannot send message, no server connection!"); Recycle(msg); return NetSendResult.FailedNotConnected; } return serverConnection.SendMessage(msg, method, sequenceChannel); }
/// <summary> /// Send a message to this exact same netpeer (loopback) /// </summary> public void SendUnconnectedToSelf(NetOutgoingMessage om) { if (om == null) throw new ArgumentNullException("msg"); if (om.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); om.m_messageType = NetMessageType.Unconnected; om.m_isSent = true; if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false) { Interlocked.Decrement(ref om.m_recyclingCount); return; // dropping unconnected message since it's not enabled for receiving } // convert outgoing to incoming NetIncomingMessage im = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, om.LengthBytes); im.Write(om); im.m_isFragment = false; im.m_receiveTime = NetTime.Now; im.m_senderConnection = null; im.m_senderEndPoint = m_socket.LocalEndPoint as NetEndPoint; NetException.Assert(im.m_bitLength == om.LengthBits); // recycle outgoing message Recycle(om); ReleaseMessage(im); }
private void ExecuteSend(double now, NetOutgoingMessage message) { int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; // must increment recycle count here, since it's decremented in QueueSendMessage and we want to keep it for the future in case or resends // we will decrement once more in DestoreMessage for final recycling Interlocked.Increment(ref message.m_recyclingCount); m_connection.QueueSendMessage(message, seqNr); int storeIndex = seqNr % m_windowSize; NetException.Assert(m_storedMessages[storeIndex].Message == null); m_storedMessages[storeIndex].NumSent++; m_storedMessages[storeIndex].Message = message; m_storedMessages[storeIndex].LastSent = now; m_storedMessages[storeIndex].SequenceNumber = seqNr; m_anyStoredResends = true; return; }
/// <summary> /// Send a message to an unconnected host /// </summary> public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<NetEndPoint> 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 (NetEndPoint ep in recipients) m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(ep, msg)); }
private void WriteLocalHail(NetOutgoingMessage om) { if (m_localHailMessage != null) { byte[] hi = m_localHailMessage.Data; if (hi != null && hi.Length >= m_localHailMessage.LengthBytes) { if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10) m_peer.ThrowOrLog("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes)); om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes); } } }
// send message immediately and recycle it internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient) { VerifyNetworkThread(); NetException.Assert(msg.m_isSent == false); bool connReset; int len = msg.Encode(m_sendBuffer, 0, 0); SendPacket(len, recipient, 1, out connReset); // no reliability, no multiple recipients - we can just recycle this message immediately msg.m_recyclingCount = 0; Recycle(msg); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) { if (remoteEndPoint == null) throw new ArgumentNullException("remoteEndPoint"); lock (m_connections) { if (m_status == NetPeerStatus.NotRunning) throw new NetException("Must call Start() first"); if (m_connectionLookup.ContainsKey(remoteEndPoint)) throw new NetException("Already connected to that endpoint!"); NetConnection hs; if (m_handshakes.TryGetValue(remoteEndPoint, out hs)) { // already trying to connect to that endpoint; make another try switch (hs.m_status) { case NetConnectionStatus.InitiatedConnect: // send another connect hs.m_connectRequested = true; break; case NetConnectionStatus.RespondedConnect: // send another response hs.SendConnectResponse(NetTime.Now, false); break; default: // weird LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status); break; } return hs; } NetConnection conn = new NetConnection(this, remoteEndPoint); conn.m_status = NetConnectionStatus.InitiatedConnect; conn.m_localHailMessage = hailMessage; // handle on network thread conn.m_connectRequested = true; conn.m_connectionInitiator = true; m_handshakes.Add(remoteEndPoint, conn); return conn; } }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) { return Connect(GetNetEndPoint(host, port), hailMessage); }
/// <summary> /// Send a message to an unconnected host /// </summary> 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 + ")"); msg.m_isSent = true; msg.m_messageType = NetMessageType.Unconnected; var adr = NetUtility.Resolve(host); if (adr == null) throw new NetException("Failed to resolve " + host); Interlocked.Increment(ref msg.m_recyclingCount); m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(new NetEndPoint(adr, port), msg)); }
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);