/// <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) return; SendMessage(msg, all, method, 0); }
/// <summary> /// Encrypt an outgoing message /// </summary> public 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; }
internal override NetSendResult Enqueue(NetOutgoingMessage message) { int queueLen = m_queuedSends.Count + 1; int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers; if (queueLen > left) return NetSendResult.Dropped; m_queuedSends.Enqueue(message); return NetSendResult.Sent; }
// 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; }
private void ExecuteSend(NetOutgoingMessage message) { m_connection.m_peer.VerifyNetworkThread(); int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; m_connection.QueueSendMessage(message, seqNr); Interlocked.Decrement(ref message.m_recyclingCount); if (message.m_recyclingCount <= 0) m_connection.m_peer.Recycle(message); return; }
/// <summary> /// Send a discovery response message /// </summary> public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint 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; m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg)); }
/// <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) 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> /// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted /// </summary> public 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> /// 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(IPEndPoint 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 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="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); }
/// <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 + ")"); 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<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg)); }
private void ExecuteSend(float now, NetOutgoingMessage message) { int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; 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; return; }
/// <summary> /// Encrypt outgoing message /// </summary> public bool Encrypt(NetOutgoingMessage msg) { try { // nested usings are fun! using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC }) { using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateEncryptor(m_key, m_iv)) { using (MemoryStream memoryStream = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write)) { cryptoStream.Write(msg.m_data, 0, msg.m_data.Length); } msg.m_data = memoryStream.ToArray(); } } } } catch { return false; } return true; }
// 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> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) { return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage); }
/// <summary> /// Puts data in message /// </summary> /// <param name="message"></param> protected override void Puts(NetOutgoingMessage message) { message.Write(this.M.Length); message.Write(this.M); }
// send message immediately internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient) { VerifyNetworkThread(); NetException.Assert(msg.m_isSent == false); bool connReset; int len = msg.Encode(m_sendBuffer, 0, 0); SendPacket(len, recipient, 1, out connReset); }
internal void Recycle(NetOutgoingMessage msg) { if (m_outgoingMessagesPool == null) return; #if DEBUG if (m_outgoingMessagesPool.Contains(msg)) throw new NetException("Recyling already recycled message! Thread race?"); #endif 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(); m_outgoingMessagesPool.Enqueue(msg); }
public void Reset() { NumSent = 0; LastSent = 0; Message = null; }
/// <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!"); return NetSendResult.FailedNotConnected; } return serverConnection.SendMessage(msg, method, sequenceChannel); }
/// <summary> /// Puts data into message /// </summary> /// <param name="message">desination</param> protected override void Puts(NetOutgoingMessage message) { message.Write(this.Username); message.Write(this.OtherData.Length); if (this.OtherData.Length > 0) message.Write(this.OtherData); Byte[] bytes = this.A.ToByteArray(); message.Write(bytes.Length); message.Write(bytes); }
/// <summary> /// Send a message to an unconnected host /// </summary> public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> 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<IPEndPoint, NetOutgoingMessage>(ep, msg)); }
internal abstract NetSendResult Send(float now, NetOutgoingMessage message);
/// <summary> /// Send a message to this exact same netpeer (loopback) /// </summary> 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); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public virtual NetConnection Connect(IPEndPoint 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((float)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> /// 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; }
// 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); }
/// <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(); byte[] storage = GetStorage(initialCapacity); retval.m_data = storage; return retval; }
/// <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); }