// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
        // seqNr is the actual nr received
        internal override void ReceiveAcknowledge(double now, int seqNr)
        {
            // late (dupe), on time or early ack?
            int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);

            if (relate < 0)
            {
                //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
                return;                 // late/duplicate ack
            }

            if (relate == 0)
            {
                //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);

                // ack arrived right on time
                NetException.Assert(seqNr == m_windowStart);

                bool resetTimeout;
                m_receivedAcks[m_windowStart] = false;
                DestoreMessage(now, m_windowStart % m_windowSize, out resetTimeout);
                m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;

                // advance window if we already have early acks
                while (m_receivedAcks.Get(m_windowStart))
                {
                    //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
                    m_receivedAcks[m_windowStart] = false;
                    bool rt;
                    DestoreMessage(now, m_windowStart % m_windowSize, out rt);
                    resetTimeout |= rt;

                    NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null);                     // should already be destored
                    m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
                    //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
                }
                if (resetTimeout)
                {
                    m_connection.ResetTimeout(now);
                }
                return;
            }

            //
            // early ack... (if it has been sent!)
            //
            // If it has been sent either the m_windowStart message was lost
            // ... or the ack for that message was lost
            //

            //m_connection.m_peer.LogDebug("Received early ack for #" + seqNr);

            int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);

            if (sendRelate <= 0)
            {
                // yes, we've sent this message - it's an early (but valid) ack
                if (m_receivedAcks[seqNr])
                {
                    // we've already destored/been acked for this message
                }
                else
                {
                    m_receivedAcks[seqNr] = true;
                }
            }
            else if (sendRelate > 0)
            {
                // uh... we haven't sent this message yet? Weird, dupe or error...
                NetException.Assert(false, "Got ack for message not yet sent?");
                return;
            }

            // Ok, lets resend all missing acks
            int rnr = seqNr;

            do
            {
                rnr--;
                if (rnr < 0)
                {
                    rnr = NetConstants.NumSequenceNumbers - 1;
                }

                if (m_receivedAcks[rnr])
                {
                    // m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)");
                }
                else
                {
                    int slot = rnr % m_windowSize;
                    NetException.Assert(m_storedMessages[slot].Message != null);
                    if (m_storedMessages[slot].NumSent == 1)
                    {
                        // just sent once; resend immediately since we found gap in ack sequence
                        NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
                        //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");

                        if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35))
                        {
                            // already resent recently
                        }
                        else
                        {
                            m_storedMessages[slot].LastSent = now;
                            m_storedMessages[slot].NumSent++;
                            m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
                            Interlocked.Increment(ref rmsg.m_recyclingCount);                             // increment this since it's being decremented in QueueSendMessage
                            m_connection.QueueSendMessage(rmsg, rnr);
                        }
                    }
                }
            } while (rnr != m_windowStart);
        }
        // on user thread
        private NetSendResult 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++;
            }

            NetSendResult retval = NetSendResult.Sent;

            int bitsPerChunk = bytesPerChunk * 8;
            int bitsLeft     = msg.LengthBits;

            for (int i = 0; i < numChunks; i++)
            {
                NetOutgoingMessage chunk = CreateMessage(0);

                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)
                {
                    var res = recipient.EnqueueMessage(chunk, method, sequenceChannel);
                    if (res == NetSendResult.Dropped)
                    {
                        Interlocked.Decrement(ref chunk.m_recyclingCount);
                    }
                    if ((int)res > (int)retval)
                    {
                        retval = res;                         // return "worst" result
                    }
                }

                bitsLeft -= bitsPerChunk;
            }

            return(retval);
        }
        private void HandleReleasedFragment(NetIncomingMessage im)
        {
            VerifyNetworkThread();

            //
            // read fragmentation header and combine fragments
            //
            int group;
            int totalBits;
            int chunkByteSize;
            int chunkNumber;
            int ptr = NetFragmentationHelper.ReadHeader(
                im.m_data, 0,
                out group,
                out totalBits,
                out chunkByteSize,
                out chunkNumber
                );

            NetException.Assert(im.LengthBytes > ptr);

            NetException.Assert(group > 0);
            NetException.Assert(totalBits > 0);
            NetException.Assert(chunkByteSize > 0);

            int totalBytes     = NetUtility.BytesToHoldBits((int)totalBits);
            int totalNumChunks = totalBytes / chunkByteSize;

            if (totalNumChunks * chunkByteSize < totalBytes)
            {
                totalNumChunks++;
            }

            NetException.Assert(chunkNumber < totalNumChunks);

            if (chunkNumber >= totalNumChunks)
            {
                LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
                return;
            }

            Dictionary <int, ReceivedFragmentGroup> groups;

            if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
            {
                groups = new Dictionary <int, ReceivedFragmentGroup>();
                m_receivedFragmentGroups[im.SenderConnection] = groups;
            }

            ReceivedFragmentGroup info;

            if (!groups.TryGetValue(group, out info))
            {
                info                = new ReceivedFragmentGroup();
                info.Data           = new byte[totalBytes];
                info.ReceivedChunks = new NetBitVector(totalNumChunks);
                groups[group]       = info;
            }

            info.ReceivedChunks[chunkNumber] = true;
            //info.LastReceived = (float)NetTime.Now;

            // copy to data
            int offset = (chunkNumber * chunkByteSize);

            Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);

            int cnt = info.ReceivedChunks.Count();

            //LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");

            LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");

            if (info.ReceivedChunks.Count() == totalNumChunks)
            {
                // Done! Transform this incoming message
                im.m_data       = info.Data;
                im.m_bitLength  = (int)totalBits;
                im.m_isFragment = false;

                LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
                groups.Remove(group);

                ReleaseMessage(im);
            }
            else
            {
                // data has been copied; recycle this incoming message
                Recycle(im);
            }

            return;
        }
示例#4
0
 private void SendCallBack(IAsyncResult res)
 {
     NetException.Assert(res.IsCompleted == true);
     m_socket.EndSendTo(res);
 }
        internal void Heartbeat(float now, uint frameCounter)
        {
            m_peer.VerifyNetworkThread();

            NetException.Assert(m_status != NetConnectionStatus.InitiatedConnect && m_status != NetConnectionStatus.RespondedConnect);

            if ((frameCounter % m_infrequentEventsSkipFrames) == 0)
            {
                if (now > m_timeoutDeadline)
                {
                    //
                    // connection timed out
                    //
                    m_peer.LogVerbose("Connection timed out at " + now + " deadline was " + m_timeoutDeadline);
                    ExecuteDisconnect("Connection timed out", true);
                    return;
                }

                // send ping?
                if (m_status == NetConnectionStatus.Connected)
                {
                    if (now > m_sentPingTime + m_peer.m_configuration.m_pingInterval)
                    {
                        SendPing();
                    }

                    // handle expand mtu
                    MTUExpansionHeartbeat(now);
                }

                if (m_disconnectRequested)
                {
                    ExecuteDisconnect(m_disconnectMessage, m_disconnectReqSendBye);
                    return;
                }
            }

            bool connectionReset;             // TODO: handle connection reset

            //
            // Note: at this point m_sendBufferWritePtr and m_sendBufferNumMessages may be non-null; resends may already be queued up
            //

            byte[] sendBuffer = m_peer.m_sendBuffer;
            int    mtu        = m_currentMTU;

            if ((frameCounter % m_messageCoalesceFrames) == 0)             // coalesce a few frames
            {
                //
                // send ack messages
                //
                while (m_queuedOutgoingAcks.Count > 0)
                {
                    int acks = (mtu - (m_sendBufferWritePtr + 5)) / 3;                     // 3 bytes per actual ack
                    if (acks > m_queuedOutgoingAcks.Count)
                    {
                        acks = m_queuedOutgoingAcks.Count;
                    }

                    NetException.Assert(acks > 0);

                    m_sendBufferNumMessages++;

                    // write acks header
                    sendBuffer[m_sendBufferWritePtr++] = (byte)NetMessageType.Acknowledge;
                    sendBuffer[m_sendBufferWritePtr++] = 0;       // no sequence number
                    sendBuffer[m_sendBufferWritePtr++] = 0;       // no sequence number
                    int len = (acks * 3) * 8;                     // bits
                    sendBuffer[m_sendBufferWritePtr++] = (byte)len;
                    sendBuffer[m_sendBufferWritePtr++] = (byte)(len >> 8);

                    // write acks
                    for (int i = 0; i < acks; i++)
                    {
                        NetTuple <NetMessageType, int> tuple;
                        m_queuedOutgoingAcks.TryDequeue(out tuple);

                        //m_peer.LogVerbose("Sending ack for " + tuple.Item1 + "#" + tuple.Item2);

                        sendBuffer[m_sendBufferWritePtr++] = (byte)tuple.Item1;
                        sendBuffer[m_sendBufferWritePtr++] = (byte)tuple.Item2;
                        sendBuffer[m_sendBufferWritePtr++] = (byte)(tuple.Item2 >> 8);
                    }

                    if (m_queuedOutgoingAcks.Count > 0)
                    {
                        // send packet and go for another round of acks
                        NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0);
                        m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connectionReset);
                        m_statistics.PacketSent(m_sendBufferWritePtr, 1);
                        m_sendBufferWritePtr    = 0;
                        m_sendBufferNumMessages = 0;
                    }
                }

                //
                // Parse incoming acks (may trigger resends)
                //
                NetTuple <NetMessageType, int> incAck;
                while (m_queuedIncomingAcks.TryDequeue(out incAck))
                {
                    //m_peer.LogVerbose("Received ack for " + acktp + "#" + seqNr);
                    NetSenderChannelBase chan = m_sendChannels[(int)incAck.Item1 - 1];

                    // If we haven't sent a message on this channel there is no reason to ack it
                    if (chan == null)
                    {
                        continue;
                    }

                    chan.ReceiveAcknowledge(now, incAck.Item2);
                }
            }

            //
            // send queued messages
            //
            if (m_peer.m_executeFlushSendQueue)
            {
                for (int i = m_sendChannels.Length - 1; i >= 0; i--)                    // Reverse order so reliable messages are sent first
                {
                    var channel = m_sendChannels[i];
                    NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
                    if (channel != null)
                    {
                        channel.SendQueuedMessages(now);
                    }
                    NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
                }
            }

            //
            // Put on wire data has been written to send buffer but not yet sent
            //
            if (m_sendBufferWritePtr > 0)
            {
                m_peer.VerifyNetworkThread();
                NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0);
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connectionReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr    = 0;
                m_sendBufferNumMessages = 0;
            }
        }
        private void Heartbeat()
        {
            VerifyNetworkThread();

            double dnow = NetTime.Now;
            float  now  = (float)dnow;

            double delta = dnow - m_lastHeartbeat;

            int maxCHBpS = 1250 - m_connections.Count;

            if (maxCHBpS < 250)
            {
                maxCHBpS = 250;
            }
            if (delta > (1.0 / (double)maxCHBpS))             // max connection heartbeats/second max
            {
                m_frameCounter++;
                m_lastHeartbeat = dnow;

                // do handshake heartbeats
                if ((m_frameCounter % 3) == 0)
                {
                    foreach (NetConnection conn in m_handshakes.Values)
                    {
                        conn.UnconnectedHeartbeat(now);
                        if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            break;                             // collection has been modified
                        }
                    }
                }

#if DEBUG
                SendDelayedPackets();
#endif

                // do connection heartbeats
                lock (m_connections)
                {
                    foreach (NetConnection conn in m_connections)
                    {
                        conn.Heartbeat(now, m_frameCounter);
                        if (conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            //
                            // remove connection
                            //
                            m_connections.Remove(conn);
                            m_connectionLookup.Remove(conn.RemoteEndpoint);
                            break;                             // can't continue iteration here
                        }
                    }
                }

                // send unsent unconnected messages
                NetTuple <IPEndPoint, NetOutgoingMessage> unsent;
                while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
                {
                    NetOutgoingMessage om = unsent.Item2;

                    bool connReset;
                    int  len = om.Encode(m_sendBuffer, 0, 0);
                    SendPacket(len, unsent.Item1, 1, out connReset);

                    Interlocked.Decrement(ref om.m_recyclingCount);
                    if (om.m_recyclingCount <= 0)
                    {
                        Recycle(om);
                    }
                }
            }

            //
            // read from socket
            //
            if (m_socket == null)
            {
                return;
            }

            if (!m_socket.Poll(1000, SelectMode.SelectRead))             // wait up to 1 ms for data to arrive
            {
                return;
            }

            //if (m_socket == null || m_socket.Available < 1)
            //	return;

            int bytesReceived = 0;
            try
            {
                bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
            }
            catch (SocketException sx)
            {
                if (sx.SocketErrorCode == SocketError.ConnectionReset)
                {
                    // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                    // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
                    // So, what to do?
                    return;
                }

                LogWarning(sx.ToString());
                return;
            }

            if (bytesReceived < NetConstants.HeaderByteSize)
            {
                return;
            }

            //LogVerbose("Received " + bytesReceived + " bytes");

            IPEndPoint ipsender = (IPEndPoint)m_senderRemote;

            NetConnection sender = null;
            m_connectionLookup.TryGetValue(ipsender, out sender);

            //
            // parse packet into messages
            //
            int ptr = 0;
            while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
            {
                // decode header
                //  8 bits - NetMessageType
                //  1 bit  - Fragment?
                // 15 bits - Sequence number
                // 16 bits - Payload length in bits

                NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++];

                byte low  = m_receiveBuffer[ptr++];
                byte high = m_receiveBuffer[ptr++];

                bool   isFragment     = ((low & 1) == 1);
                ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7));

                ushort payloadBitLength  = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
                int    payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);

                if (bytesReceived - ptr < payloadByteLength)
                {
                    LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr));
                    return;
                }

                try
                {
                    NetException.Assert(tp <NetMessageType.Unused1 || tp> NetMessageType.Unused29);

                    if (tp >= NetMessageType.LibraryError)
                    {
                        if (sender != null)
                        {
                            sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
                        }
                        else
                        {
                            ReceivedUnconnectedLibraryMessage(ipsender, tp, ptr, payloadByteLength);
                        }
                    }
                    else
                    {
                        if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData))
                        {
                            return;                             // dropping unconnected message since it's not enabled
                        }
                        NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
                        msg.m_isFragment          = isFragment;
                        msg.m_sequenceNumber      = sequenceNumber;
                        msg.m_receivedMessageType = tp;
                        msg.m_senderConnection    = sender;
                        msg.m_senderEndpoint      = ipsender;
                        msg.m_bitLength           = payloadBitLength;
                        Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
                        if (sender != null)
                        {
                            if (tp == NetMessageType.Unconnected)
                            {
                                // We're connected; but we can still send unconnected messages to this peer
                                msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                                ReleaseMessage(msg);
                            }
                            else
                            {
                                // connected application (non-library) message
                                sender.ReceivedMessage(msg);
                            }
                        }
                        else
                        {
                            // at this point we know the message type is enabled
                            // unconnected application (non-library) message
                            msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                            ReleaseMessage(msg);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Packet parsing error: " + ex.Message + " from " + ipsender);
                }
                ptr += payloadByteLength;
            }
        }
示例#7
0
        internal override void ReceiveMessage(NetIncomingMessage message)
        {
            int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);

            // ack no matter what
            m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);

            if (relate == 0)
            {
                // Log("Received message #" + message.SequenceNumber + " right on time");

                //
                // excellent, right on time
                //
                //m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);

                AdvanceWindow();
                m_peer.ReleaseMessage(message);

                // release withheld messages
                int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;

                while (m_earlyReceived[nextSeqNr % m_windowSize])
                {
                    message = m_withheldMessages[nextSeqNr % m_windowSize];
                    NetException.Assert(message != null);

                    // remove it from withheld messages
                    m_withheldMessages[nextSeqNr % m_windowSize] = null;

                    m_peer.LogVerbose("Releasing withheld message #" + message);

                    m_peer.ReleaseMessage(message);

                    AdvanceWindow();
                    nextSeqNr++;
                }

                return;
            }

            if (relate < 0)
            {
                // duplicate
                m_connection.m_statistics.MessageDropped();
                m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
                return;
            }

            // relate > 0 = early message
            if (relate > m_windowSize)
            {
                // too early message!
                m_connection.m_statistics.MessageDropped();
                m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
                return;
            }

            m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
            m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
            m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
        }
示例#8
0
        /// <summary>
        /// Получает большую книгу / по указанному индексу.
        /// </summary>
        public bool Get(int bitIndex)
        {
            NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);

            return((m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0);
        }
示例#9
0
 /// <summary>
 /// Устанавливает все биты / булевы к нулю / ложной.
 /// </summary>
 public void Clear()
 {
     Array.Clear(m_data, 0, m_data.Length);
     m_numBitsSet = 0;
     NetException.Assert(this.IsEmpty());
 }
示例#10
0
        /// <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;
        }
        // remoteWindowStart is remote expected sequence number; everything below this has arrived properly
        // seqNr is the actual nr received
        internal override void ReceiveAcknowledge(float now, int seqNr)
        {
            // late (dupe), on time or early ack?
            int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);

            if (relate < 0)
            {
                return;                 // late/duplicate ack
            }
            if (relate == 0)
            {
                // ack arrived right on time
                NetException.Assert(seqNr == m_windowStart);

                m_receivedAcks[m_windowStart] = false;
                DestoreMessage(m_windowStart % m_windowSize);
                m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;

                // advance window if we already have early acks
                while (m_receivedAcks.Get(m_windowStart))
                {
                    m_receivedAcks[m_windowStart] = false;
                    DestoreMessage(m_windowStart % m_windowSize);

                    NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null);                     // should already be destored
                    m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
                }

                return;
            }

            //
            // early ack... (if it has been sent!)
            //
            // If it has been sent either the m_windowStart message was lost
            // ... or the ack for that message was lost
            //

            int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);

            if (sendRelate <= 0)
            {
                // yes, we've sent this message - it's an early (but valid) ack
                if (m_receivedAcks[seqNr])
                {
                    // we've already destored/been acked for this message
                }
                else
                {
                    m_receivedAcks[seqNr] = true;
                }
            }
            else if (sendRelate > 0)
            {
                // uh... we haven't sent this message yet? Weird, dupe or error...
                NetException.Assert(false, "Got ack for message not yet sent?");
                return;
            }

            // Ok, lets resend all missing acks
            int rnr = seqNr;

            do
            {
                rnr--;
                if (rnr < 0)
                {
                    rnr = NetConstants.NumSequenceNumbers - 1;
                }

                if (!m_receivedAcks[rnr])
                {
                    int slot = rnr % m_windowSize;
                    NetException.Assert(m_storedMessages[slot].Message != null);
                    if (m_storedMessages[slot].NumSent == 1)
                    {
                        // just sent once; resend immediately since we found gap in ack sequence
                        NetOutgoingMessage rmsg = m_storedMessages[slot].Message;

                        if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f))
                        {
                            // already resent recently
                        }
                        else
                        {
                            m_storedMessages[slot].LastSent = now;
                            m_storedMessages[slot].NumSent++;
                            m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
                            m_connection.QueueSendMessage(rmsg, rnr);
                        }
                    }
                }
            } while (rnr != m_windowStart);
        }
示例#12
0
        internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
        {
            // NOTE: I had to change the header format so it won't collide with multiplexed STUN on the same socket
            //       and I made it big-endian for consistency's sake. -AR
            //
            //  <- MSB                                                     LSB ->
            //   0                   1                   2                   3
            //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            //  V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V
            //  |1|F| Sequence Number           |NetMessageType |  Length bits...
            //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            //  ...             |
            //  +-+-+-+-+-+-+-+-+
            //
            // IMPORTANT: The packet format is also encoded elsewhere, to send NetMessageType.Acknowledge (see NetConnection.cs)
            //

            intoBuffer[ptr++] = (byte)(((sequenceNumber >> 8) & 0x3F) | (m_fragmentGroup == 0 ? 0x80 : 0xC0));
            intoBuffer[ptr++] = (byte)sequenceNumber;

            intoBuffer[ptr++] = (byte)m_messageType;

            if (m_fragmentGroup == 0)
            {
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
                intoBuffer[ptr++] = (byte)m_bitLength;


                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }
            else
            {
                int wasPtr = ptr;
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
                intoBuffer[ptr++] = (byte)m_bitLength;


                //
                // write fragmentation header
                //
                ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
                int hdrLen = ptr - wasPtr - 2;

                // update length
                int realBitLength = m_bitLength + (hdrLen * 8);
                intoBuffer[wasPtr]     = (byte)(realBitLength >> 8);
                intoBuffer[wasPtr + 1] = (byte)realBitLength;

                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }

            NetException.Assert(ptr > 0);
            return(ptr);
        }
示例#13
0
        private void Heartbeat()
        {
            VerifyNetworkThread();

            double dnow = NetTime.Now;
            float  now  = (float)dnow;

            double delta = dnow - m_lastHeartbeat;

            int maxCHBpS = 1250 - m_connections.Count;

            if (maxCHBpS < 250)
            {
                maxCHBpS = 250;
            }
            if (delta > (1.0 / (double)maxCHBpS) || delta < 0.0) // max connection heartbeats/second max
            {
                m_frameCounter++;
                m_lastHeartbeat = dnow;

                // do handshake heartbeats
                if ((m_frameCounter % 3) == 0)
                {
                    foreach (var kvp in m_handshakes)
                    {
                        NetConnection conn = kvp.Value as NetConnection;
#if DEBUG
                        // sanity check
                        if (kvp.Key != kvp.Key)
                        {
                            LogWarning("Sanity fail! Connection in handshake list under wrong key!");
                        }
#endif
                        conn.UnconnectedHeartbeat(now);
                        if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected)
                        {
#if DEBUG
                            // sanity check
                            if (conn.m_status == NetConnectionStatus.Disconnected && m_handshakes.ContainsKey(conn.RemoteEndPoint))
                            {
                                LogWarning("Sanity fail! Handshakes list contained disconnected connection!");
                                m_handshakes.Remove(conn.RemoteEndPoint);
                            }
#endif
                            break; // collection has been modified
                        }
                    }
                }

#if DEBUG
                SendDelayedPackets();
#endif

                // update m_executeFlushSendQueue
                if (m_configuration.m_autoFlushSendQueue)
                {
                    m_executeFlushSendQueue = true;
                }

                // do connection heartbeats
                lock (m_connections)
                {
                    foreach (NetConnection conn in m_connections)
                    {
                        conn.Heartbeat(now, m_frameCounter);
                        if (conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            //
                            // remove connection
                            //
                            m_connections.Remove(conn);
                            m_connectionLookup.Remove(conn.RemoteEndPoint);
                            break; // can't continue iteration here
                        }
                    }
                }
                m_executeFlushSendQueue = false;

                // send unsent unconnected messages
                NetTuple <IPEndPoint, NetOutgoingMessage> unsent;
                while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
                {
                    NetOutgoingMessage om = unsent.Item2;

                    bool connReset;
                    int  len = om.Encode(m_sendBuffer, 0, 0);
                    SendPacket(len, unsent.Item1, 1, out connReset);

                    Interlocked.Decrement(ref om.m_recyclingCount);
                    if (om.m_recyclingCount <= 0)
                    {
                        Recycle(om);
                    }
                }
            }

            // Would rather call this after reading, but a many things return (that probably shouldn't -AR) below here, so we call it now.
            UpdateStun();

            //
            // read from socket
            //
            if (m_socket == null)
            {
                return;
            }

            if (!m_socket.Poll(1000, SelectMode.SelectRead)) // wait up to 1 ms for data to arrive
            {
                return;
            }

            //if (m_socket == null || m_socket.Available < 1)
            //    return;

            // update now
            dnow = NetTime.Now;
            now  = (float)dnow;

            do
            {
                int bytesReceived = 0;
                try
                {
                    bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
                }
                catch (SocketException sx)
                {
                    if (sx.SocketErrorCode == SocketError.ConnectionReset)
                    {
                        // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                        // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
                        // So, what to do?
                        LogWarning("ConnectionReset");
                        return;
                    }

                    LogWarning(sx.ToString());
                    return;
                }

                if (MaybeHandleStunPacket(m_receiveBuffer, 0, bytesReceived))
                {
                    continue;
                }

                if (bytesReceived < NetConstants.HeaderByteSize)
                {
                    return;
                }

                //LogVerbose("Received " + bytesReceived + " bytes");

                if (m_upnp != null && now < m_upnp.m_discoveryResponseDeadline)
                {
                    // is this an UPnP response?
                    try
                    {
                        string resp = System.Text.Encoding.ASCII.GetString(m_receiveBuffer, 0, bytesReceived);
                        if (resp.Contains("upnp:rootdevice") || resp.Contains("UPnP/1.0"))
                        {
                            resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
                            resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
                            m_upnp.ExtractServiceUrl(resp);
                            return;
                        }
                    }
                    catch { }
                }


                IPEndPoint    ipsender = (IPEndPoint)m_senderRemote;
                NetConnection sender   = null;
                m_connectionLookup.TryGetValue(ipsender, out sender);

                //
                // parse packet into messages
                //
                int numMessages = 0;
                int ptr         = 0;
                while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
                {
                    // NOTE: I had to change the header format so it won't collide with multiplexed STUN on the same socket
                    //       and I made it big-endian for consistency's sake. -AR
                    //
                    //  <- MSB                                                     LSB ->
                    //   0                   1                   2                   3
                    //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                    //  V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V-+-+-+-+-+-+-+-V
                    //  |1|F| Sequence Number           |NetMessageType |  Length bits...
                    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                    //  ...             |
                    //  +-+-+-+-+-+-+-+-+
                    //

                    numMessages++;

                    byte seqHi = m_receiveBuffer[ptr++];
                    byte seqLo = m_receiveBuffer[ptr++];

                    bool   isValid        = ((seqHi & 0x80) != 0);
                    bool   isFragment     = ((seqHi & 0x40) != 0);
                    ushort sequenceNumber = (ushort)(((seqHi & 0x3F) << 8) | seqLo);

                    if (!isValid)
                    {
                        LogVerbose("Non-Lidgren packet");
                        return;
                    }

                    NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++];

                    ushort payloadBitLength  = (ushort)((m_receiveBuffer[ptr++] << 8) | m_receiveBuffer[ptr++]);
                    int    payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);

                    if (bytesReceived - ptr < payloadByteLength)
                    {
                        LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr));
                        return;
                    }

                    try
                    {
                        NetException.Assert(tp <NetMessageType.Unused1 || tp> NetMessageType.Unused29);

                        if (tp >= NetMessageType.LibraryError)
                        {
                            if (sender != null)
                            {
                                sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
                            }
                            else
                            {
                                ReceivedUnconnectedLibraryMessage(dnow, ipsender, tp, ptr, payloadByteLength);
                            }
                        }
                        else
                        {
                            if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData))
                            {
                                return; // dropping unconnected message since it's not enabled
                            }
                            NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
                            msg.m_isFragment          = isFragment;
                            msg.m_receiveTime         = dnow;
                            msg.m_sequenceNumber      = sequenceNumber;
                            msg.m_receivedMessageType = tp;
                            msg.m_senderConnection    = sender;
                            msg.m_senderEndPoint      = ipsender;
                            msg.m_bitLength           = payloadBitLength;
                            Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
                            if (sender != null)
                            {
                                if (tp == NetMessageType.Unconnected)
                                {
                                    // We're connected; but we can still send unconnected messages to this peer
                                    msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                                    ReleaseMessage(msg);
                                }
                                else
                                {
                                    // connected application (non-library) message
                                    sender.ReceivedMessage(msg);
                                }
                            }
                            else
                            {
                                // at this point we know the message type is enabled
                                // unconnected application (non-library) message
                                msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                                ReleaseMessage(msg);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogError("Packet parsing error: " + ex.Message + " from " + ipsender);
                    }
                    ptr += payloadByteLength;
                }

                m_statistics.PacketReceived(bytesReceived, numMessages);
                if (sender != null)
                {
                    sender.m_statistics.PacketReceived(bytesReceived, numMessages);
                }
            } while (m_socket.Available > 0);
        }
        internal int Encode(byte[] intoBuffer, int ptr, long connectionId, int sequenceNumber)
        {
            //  8 bits - NetMessageType
            // 64 bits - Connection ID
            //  1 bit  - Fragment?
            // 15 bits - Sequence number
            // 16 bits - Payload length in bits

            intoBuffer[ptr++] = (byte)m_messageType;
            intoBuffer[ptr++] = (byte)((connectionId >> 56) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 48) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 40) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 32) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 24) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 16) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId >> 8) & 0xFF);
            intoBuffer[ptr++] = (byte)((connectionId) & 0xFF);
            byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));

            intoBuffer[ptr++] = low;
            intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);

            if (m_fragmentGroup == 0)
            {
                intoBuffer[ptr++] = (byte)m_bitLength;
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);

                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }
            else
            {
                int wasPtr = ptr;
                intoBuffer[ptr++] = (byte)m_bitLength;
                intoBuffer[ptr++] = (byte)(m_bitLength >> 8);

                //
                // write fragmentation header
                //
                ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
                int hdrLen = ptr - wasPtr - 2;

                // update length
                int realBitLength = m_bitLength + (hdrLen * 8);
                intoBuffer[wasPtr]     = (byte)realBitLength;
                intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);

                int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
                if (byteLen > 0)
                {
                    Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
                    ptr += byteLen;
                }
            }

            NetException.Assert(ptr > 0);
            return(ptr);
        }
        // call this regularely
        internal override void SendQueuedMessages(double now)
        {
            //
            // resends
            //
            m_anyStoredResends = false;
            for (int i = 0; i < m_storedMessages.Length; i++)
            {
                var storedMsg         = m_storedMessages[i];
                NetOutgoingMessage om = storedMsg.Message;
                if (om == null)
                {
                    continue;
                }

                m_anyStoredResends = true;

                double t = storedMsg.LastSent;
                if (t > 0 && (now - t) > m_resendDelay)
                {
                    // deduce sequence number

                    /*
                     * int startSlot = m_windowStart % m_windowSize;
                     * int seqNr = m_windowStart;
                     * while (startSlot != i)
                     * {
                     *      startSlot--;
                     *      if (startSlot < 0)
                     *              startSlot = m_windowSize - 1;
                     *      seqNr--;
                     * }
                     */

                    //m_connection.m_peer.LogVerbose("Resending due to delay #" + m_storedMessages[i].SequenceNumber + " " + om.ToString());
                    m_connection.m_statistics.MessageResent(MessageResendReason.Delay);

                    Interlocked.Increment(ref om.m_recyclingCount);                     // increment this since it's being decremented in QueueSendMessage
                    m_connection.QueueSendMessage(om, storedMsg.SequenceNumber);

                    m_storedMessages[i].LastSent = now;
                    m_storedMessages[i].NumSent++;
                }
            }

            int num = GetAllowedSends();

            if (num < 1)
            {
                return;
            }

            // queued sends
            while (num > 0 && m_queuedSends.Count > 0)
            {
                NetOutgoingMessage om;
                if (m_queuedSends.TryDequeue(out om))
                {
                    ExecuteSend(now, om);
                }
                num--;
                NetException.Assert(num == GetAllowedSends());
            }
        }
        private void Heartbeat()
        {
            VerifyNetworkThread();

            double dnow = NetTime.Now;
            float  now  = (float)dnow;

            double delta = dnow - m_lastHeartbeat;

            int maxCHBpS = 1250 - m_connections.Count;

            if (maxCHBpS < 250)
            {
                maxCHBpS = 250;
            }
            if (delta > (1.0 / (double)maxCHBpS) || delta < 0.0)             // max connection heartbeats/second max
            {
                m_frameCounter++;
                m_lastHeartbeat = dnow;

                // do handshake heartbeats
                if ((m_frameCounter % 3) == 0)
                {
                    foreach (var kvp in m_handshakes)
                    {
                        NetConnection conn = kvp.Value as NetConnection;
#if DEBUG
                        // sanity check
                        if (kvp.Key != kvp.Key)
                        {
                            LogWarning("Sanity fail! Connection in handshake list under wrong key!");
                        }
#endif
                        conn.UnconnectedHeartbeat(now);
                        if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected)
                        {
#if DEBUG
                            // sanity check
                            if (conn.m_status == NetConnectionStatus.Disconnected && m_handshakes.ContainsKey(conn.RemoteEndPoint))
                            {
                                LogWarning("Sanity fail! Handshakes list contained disconnected connection!");
                                m_handshakes.Remove(conn.RemoteEndPoint);
                            }
#endif
                            break;                             // collection has been modified
                        }
                    }
                }

#if DEBUG
                SendDelayedPackets();
#endif

                // update m_executeFlushSendQueue
                if (m_configuration.m_autoFlushSendQueue)
                {
                    m_executeFlushSendQueue = true;
                }

                // do connection heartbeats
                lock (m_connections)
                {
                    foreach (NetConnection conn in m_connections)
                    {
                        conn.Heartbeat(now, m_frameCounter);
                        if (conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            //
                            // remove connection
                            //
                            m_connections.Remove(conn);
                            m_connectionLookup.Remove(conn.RemoteEndPoint);
                            break;                             // can't continue iteration here
                        }
                    }
                }
                m_executeFlushSendQueue = false;

                // send unsent unconnected messages
                NetTuple <IPEndPoint, NetOutgoingMessage> unsent;
                while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
                {
                    NetOutgoingMessage om = unsent.Item2;

                    bool connReset;
                    int  len = om.Encode(m_sendBuffer, 0, 0);
                    SendPacket(len, unsent.Item1, 1, out connReset);

                    Interlocked.Decrement(ref om.m_recyclingCount);
                    if (om.m_recyclingCount <= 0)
                    {
                        Recycle(om);
                    }
                }
            }

            //
            // read from socket
            //
            if (m_socket == null)
            {
                return;
            }

            if (!m_socket.Poll(1000, SelectMode.SelectRead))             // wait up to 1 ms for data to arrive
            {
                return;
            }

            do
            {
                int bytesReceived = 0;
                try
                {
                    bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
                }
                catch (SocketException sx)
                {
                    if (sx.SocketErrorCode == SocketError.ConnectionReset)
                    {
                        // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                        // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
                        // So, what to do?
                        LogWarning("ConnectionReset");
                        return;
                    }

                    LogWarning(sx.ToString());
                    return;
                }

                if (bytesReceived < NetConstants.HeaderByteSize)
                {
                    return;
                }

                IPEndPoint ipsender = (IPEndPoint)m_senderRemote;

                if (ipsender.Port == 1900)
                {
                    // UPnP response
                    try
                    {
                        string resp = System.Text.Encoding.ASCII.GetString(m_receiveBuffer, 0, bytesReceived);
                        if (resp.Contains("upnp:rootdevice"))
                        {
                            resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
                            resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
                            m_upnp.ExtractServiceUrl(resp);
                            return;
                        }
                    }
                    catch { }
                }

                NetConnection sender = null;
                m_connectionLookup.TryGetValue(ipsender, out sender);

                double receiveTime = NetTime.Now;
                //
                // parse packet into messages
                //
                int numMessages = 0;
                int ptr         = 0;
                while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
                {
                    // decode header
                    //  8 bits - NetMessageType
                    //  1 bit  - Fragment?
                    // 15 bits - Sequence number
                    // 16 bits - Payload length in bits

                    numMessages++;

                    NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++];

                    byte low  = m_receiveBuffer[ptr++];
                    byte high = m_receiveBuffer[ptr++];

                    bool   isFragment     = ((low & 1) == 1);
                    ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7));

                    ushort payloadBitLength  = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
                    int    payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);

                    if (bytesReceived - ptr < payloadByteLength)
                    {
                        LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr));
                        return;
                    }

                    try
                    {
                        NetException.Assert(tp <NetMessageType.Unused1 || tp> NetMessageType.Unused29);

                        if (tp >= NetMessageType.LibraryError)
                        {
                            if (sender != null)
                            {
                                sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
                            }
                            else
                            {
                                ReceivedUnconnectedLibraryMessage(receiveTime, ipsender, tp, ptr, payloadByteLength);
                            }
                        }
                        else
                        {
                            if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData))
                            {
                                return;                                 // dropping unconnected message since it's not enabled
                            }
                            NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
                            msg.m_isFragment          = isFragment;
                            msg.m_receiveTime         = receiveTime;
                            msg.m_sequenceNumber      = sequenceNumber;
                            msg.m_receivedMessageType = tp;
                            msg.m_senderConnection    = sender;
                            msg.m_senderEndPoint      = ipsender;
                            msg.m_bitLength           = payloadBitLength;
                            Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
                            if (sender != null)
                            {
                                if (tp == NetMessageType.Unconnected)
                                {
                                    // We're connected; but we can still send unconnected messages to this peer
                                    msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                                    ReleaseMessage(msg);
                                }
                                else
                                {
                                    // connected application (non-library) message
                                    sender.ReceivedMessage(msg);
                                }
                            }
                            else
                            {
                                // at this point we know the message type is enabled
                                // unconnected application (non-library) message
                                msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
                                ReleaseMessage(msg);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogError("Packet parsing error: " + ex.Message + " from " + ipsender);
                    }
                    ptr += payloadByteLength;
                }

                m_statistics.PacketReceived(bytesReceived, numMessages);
                if (sender != null)
                {
                    sender.m_statistics.PacketReceived(bytesReceived, numMessages);
                }
            } while (m_socket.Available > 0);
        }
 /// <summary>
 /// Reads the specified number of bits into an Int64 without advancing the read pointer
 /// </summary>
 public Int64 PeekInt64(int numberOfBits)
 {
     NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
     return((long)PeekUInt64(numberOfBits));
 }
示例#18
0
        internal void Heartbeat(float now, uint frameCounter)
        {
            m_peer.VerifyNetworkThread();

            NetException.Assert(m_status != NetConnectionStatus.InitiatedConnect && m_status != NetConnectionStatus.RespondedConnect);

            if ((frameCounter % 5) == 0)
            {
                if (now > m_timeoutDeadline)
                {
                    //
                    // connection timed out
                    //
                    m_peer.LogVerbose("Connection timed out at " + now + " deadline was " + m_timeoutDeadline);
                    ExecuteDisconnect("Connection timed out", true);
                }

                // send ping?
                if (m_status == NetConnectionStatus.Connected)
                {
                    if (now > m_sentPingTime + m_peer.m_configuration.m_pingInterval)
                    {
                        SendPing();
                    }
                }

                if (m_disconnectRequested)
                {
                    ExecuteDisconnect(m_disconnectMessage, true);
                    return;
                }
            }

            bool connectionReset;             // TODO: handle connection reset

            //
            // Note: at this point m_sendBufferWritePtr and m_sendBufferNumMessages may be non-null; resends may already be queued up
            //

            byte[] sendBuffer = m_peer.m_sendBuffer;
            int    mtu        = m_peerConfiguration.m_maximumTransmissionUnit;

            if ((frameCounter % 3) == 0)             // coalesce a few frames
            {
                //
                // send ack messages
                //
                while (m_queuedAcks.Count > 0)
                {
                    int acks = (mtu - (m_sendBufferWritePtr + 5)) / 3;                     // 3 bytes per actual ack
                    if (acks > m_queuedAcks.Count)
                    {
                        acks = m_queuedAcks.Count;
                    }

                    NetException.Assert(acks > 0);

                    m_sendBufferNumMessages++;

                    // write acks header
                    sendBuffer[m_sendBufferWritePtr++] = (byte)NetMessageType.Acknowledge;
                    sendBuffer[m_sendBufferWritePtr++] = 0;       // no sequence number
                    sendBuffer[m_sendBufferWritePtr++] = 0;       // no sequence number
                    int len = (acks * 3) * 8;                     // bits
                    sendBuffer[m_sendBufferWritePtr++] = (byte)len;
                    sendBuffer[m_sendBufferWritePtr++] = (byte)(len >> 8);

                    // write acks
                    for (int i = 0; i < acks; i++)
                    {
                        NetTuple <NetMessageType, int> tuple;
                        m_queuedAcks.TryDequeue(out tuple);

                        //m_peer.LogVerbose("Sending ack for " + tuple.Item1 + "#" + tuple.Item2);

                        sendBuffer[m_sendBufferWritePtr++] = (byte)tuple.Item1;
                        sendBuffer[m_sendBufferWritePtr++] = (byte)tuple.Item2;
                        sendBuffer[m_sendBufferWritePtr++] = (byte)(tuple.Item2 >> 8);
                    }

                    if (m_queuedAcks.Count > 0)
                    {
                        // send packet and go for another round of acks
                        NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0);
                        m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndpoint, m_sendBufferNumMessages, out connectionReset);
                        m_statistics.PacketSent(m_sendBufferWritePtr, 1);
                        m_sendBufferWritePtr    = 0;
                        m_sendBufferNumMessages = 0;
                    }
                }
            }

            //
            // send queued messages
            //
            for (int i = m_sendChannels.Length - 1; i >= 0; i--)                // Reverse order so reliable messages are sent first
            {
                var channel = m_sendChannels[i];
                NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
                if (channel != null)
                {
                    channel.SendQueuedMessages(now);
                }
                NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
            }

            //
            // Put on wire data has been written to send buffer but not yet sent
            //
            if (m_sendBufferWritePtr > 0)
            {
                m_peer.VerifyNetworkThread();
                NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0);
                m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndpoint, m_sendBufferNumMessages, out connectionReset);
                m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
                m_sendBufferWritePtr    = 0;
                m_sendBufferNumMessages = 0;
            }
        }