예제 #1
0
        /// <summary>
        /// Creates an incoming message with the required capacity for releasing to the application
        /// </summary>
        internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int requiredCapacity)
        {
            NetIncomingMessage retval;

            if (m_incomingMessagesPool.TryDequeue(out retval))
            {
                retval.Reset();
            }
            else
            {
                retval = new NetIncomingMessage();
            }

            NetException.Assert(retval.m_status == NetIncomingMessageReleaseStatus.NotReleased);

            retval.m_incomingType     = tp;
            retval.m_senderConnection = null;
            retval.m_senderEndpoint   = null;
            retval.m_status           = NetIncomingMessageReleaseStatus.NotReleased;

            if (requiredCapacity > 0)
            {
                byte[] storage = GetStorage(requiredCapacity);
                retval.m_data = storage;
            }
            else
            {
                retval.m_data = null;
            }

            return(retval);
        }
예제 #2
0
        internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
        {
            NetIncomingMessage retval;

            if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
            {
                retval = new NetIncomingMessage(tp);
            }
            else
            {
                retval.m_incomingMessageType = tp;
            }
            retval.m_data = useStorageData;
            return(retval);
        }
        internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, ArraySegment <byte> useStorageData)
        {
            NetIncomingMessage retval;

            if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
            {
                retval = new NetIncomingMessage(tp);
            }
            else
            {
                retval.m_incomingMessageType = tp;
            }
            retval.m_buf = useStorageData.Array;
            return(retval);
        }
예제 #4
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);
        }
예제 #5
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();
            }

            NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount);

            if (initialCapacity > 0)
            {
                retval.m_data = GetStorage(initialCapacity);
            }

            return(retval);
        }
        private void ProcessConnectionUpdates(double now)
        {
            // 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);
            }
        }
예제 #7
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 (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;
            }

            //if (m_socket == null || m_socket.Available < 1)
            //	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;
                }

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

                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);
        }
        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;
            }
        }
예제 #9
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;
                }
            }
        }
예제 #10
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);
                    //UnityEngine.Debug.Log($"recv from {m_senderRemote}, bytes {bytesReceived}, local {m_socket.LocalEndPoint}");
                }
                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);
        }
예제 #11
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;
            }
        }