/// <summary>
        /// Peer update
        /// </summary>
        public void PeerUpdate()
        {
            int maxCHBpS = 1250;

            if (maxCHBpS < 250)
            {
                maxCHBpS = 250;
            }

            double now   = NetTime.Now;
            double delta = now - m_lastHeartbeat;

            m_frameCounter++;
            m_lastHeartbeat = now;

            if (delta > (1.0 / maxCHBpS) || delta < 0.0)
            {
                ProcessHandshakes(delta, now, maxCHBpS);
                FlushNetwork();
                ProcessConnectionUpdates(now);

                /*#if DEBUG
                 * SendDelayedPackets();
                 #endif*/
            }

            m_upnp?.CheckForDiscoveryTimeout();
        }
Esempio n. 2
0
        private void Heartbeat()
        {
            VerifyNetworkThread();

            double now   = NetTime.Now;
            double delta = now - 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 = now;

                // 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_needFlushSendQueue == true)
                {
                    m_executeFlushSendQueue = true;
                    m_needFlushSendQueue    = false;                  // a race condition to this variable will simply result in a single superfluous call to FlushSendQueue()
                }

                // do connection heartbeats
                lock (m_connections)
                {
                    for (int i = m_connections.Count - 1; i >= 0; i--)
                    {
                        var conn = m_connections[i];
                        conn.Heartbeat(now, m_frameCounter);
                        if (conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            //
                            // remove connection
                            //
                            m_connections.RemoveAt(i);
                            m_connectionLookup.Remove(conn.RemoteEndPoint);
                        }
                    }
                }
                m_executeFlushSendQueue = false;

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

                    int len = om.Encode(m_sendBuffer, 0, 0);

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

                    bool connReset;
                    SendPacket(len, unsent.Item1, 1, out connReset);
                }
            }

            if (m_upnp != null)
            {
                m_upnp.CheckForDiscoveryTimeout();
            }

            //
            // 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
            now = NetTime.Now;

            do
            {
                int bytesReceived = 0;
                try
                {
                    bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
                }
                catch (SocketException sx)
                {
                    switch (sx.SocketErrorCode)
                    {
                    case 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;

                    case SocketError.NotConnected:
                        // socket is unbound; try to rebind it (happens on mobile when process goes to sleep)
                        BindSocket(true);
                        return;

                    default:
                        LogWarning("Socket exception: " + sx.ToString());
                        return;
                    }
                }

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

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

                var ipsender = (NetEndPoint)m_senderRemote;

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

                            // don't try to parse this packet further
                            return;
                        }
                    }
                }

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

                //
                // parse packet into messages
                //
                int numMessages  = 0;
                int numFragments = 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));

                    if (isFragment)
                    {
                        numFragments++;
                    }

                    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;
                    }

                    if (tp >= NetMessageType.Unused1 && tp <= NetMessageType.Unused29)
                    {
                        ThrowOrLog("Unexpected NetMessageType: " + tp);
                        return;
                    }

                    try
                    {
                        if (tp >= NetMessageType.LibraryError)
                        {
                            if (sender != null)
                            {
                                sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
                            }
                            else
                            {
                                ReceivedUnconnectedLibraryMessage(now, 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         = now;
                            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, numFragments);
                if (sender != null)
                {
                    sender.m_statistics.PacketReceived(bytesReceived, numMessages, numFragments);
                }
            } while (m_socket.Available > 0);
        }
Esempio n. 3
0
        private void Heartbeat()
        {
            VerifyNetworkThread();

            double now   = NetTime.Now;
            double delta = now - 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 = now;

                // 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_needFlushSendQueue == true)
                {
                    m_executeFlushSendQueue = true;
                    m_needFlushSendQueue    = false;                  // a race condition to this variable will simply result in a single superfluous call to FlushSendQueue()
                }

                // do connection heartbeats
                lock (m_connections)
                {
                    for (int i = m_connections.Count - 1; i >= 0; i--)
                    {
                        var conn = m_connections[i];
                        conn.Heartbeat(now, m_frameCounter);
                        if (conn.m_status == NetConnectionStatus.Disconnected)
                        {
                            //
                            // remove connection
                            //
                            m_connections.RemoveAt(i);
                            m_connectionLookup.Remove(conn.RemoteEndPoint);
                        }
                    }
                }
                m_executeFlushSendQueue = false;

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

                    int len = om.Encode(m_sendBuffer, 0, 0);

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

                    bool connReset;
                    SendPacket(len, unsent.Item1, 1, out connReset);
                }
            }

            if (m_upnp != null)
            {
                m_upnp.CheckForDiscoveryTimeout();
            }

            //
            // 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
            now = NetTime.Now;

            try
            {
                do
                {
                    ReceiveSocketData(now);
                } while (m_socket.Available > 0);
            }
            catch (SocketException sx)
            {
                switch (sx.SocketErrorCode)
                {
                case 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;

                case SocketError.NotConnected:
                    // socket is unbound; try to rebind it (happens on mobile when process goes to sleep)
                    BindSocket(true);
                    return;

                default:
                    LogWarning("Socket exception: " + sx.ToString());
                    return;
                }
            }
        }