Пример #1
0
            /// <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));
            }
Пример #7
0
            /// <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;
            }
Пример #9
0
            /// <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;
            }
Пример #16
0
 /// <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);
 }
Пример #18
0
            // 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;
 }
Пример #21
0
            /// <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);
            }
Пример #26
0
            /// <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);
 }