internal void StoreMessage(double now, OutgoingNetMessage msg)
        {
            int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered;

            List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx];

            if (list == null)
            {
                list = new List <OutgoingNetMessage>();
                m_storedMessages[chanBufIdx] = list;
            }
            list.Add(msg);

            // schedule resend
            double nextResend = now + m_owner.Configuration.ResendFunction(msg.m_numSent, m_currentAvgRoundtrip);

            msg.m_nextResend = nextResend;

            // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Try no " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray()));

            m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this);

            // earliest?
            if (nextResend < m_earliestResend[chanBufIdx])
            {
                m_earliestResend[chanBufIdx] = nextResend;
            }
        }
Exemple #2
0
        internal void StoreMessage(double now, OutgoingNetMessage msg)
        {
            int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered;

            List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx];

            if (list == null)
            {
                list = new List <OutgoingNetMessage>();
                m_storedMessages[chanBufIdx] = list;
            }
            list.Add(msg);

            // schedule resend
            float  multiplier = (1 + (msg.m_numSent * msg.m_numSent)) * m_owner.m_config.m_resendTimeMultiplier;
            double nextResend = now + (0.025f + (float)m_currentAvgRoundtrip * 1.1f * multiplier);

            msg.m_nextResend = nextResend;

            m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this);

            // earliest?
            if (nextResend < m_earliestResend[chanBufIdx])
            {
                m_earliestResend[chanBufIdx] = nextResend;
            }
        }
        internal void AddConnection(double now, NetConnection conn)
        {
            conn.SetStatus(NetConnectionStatus.Connecting, "Connecting");

            lock (m_connections)
            {
                if (m_connections.Contains(conn))
                {
                    // already added
                    conn.m_approved = true;                     // just to be sure
                    return;
                }
            }

            LogWrite("Adding connection " + conn);

            // send response; even if connected
            OutgoingNetMessage response = CreateSystemMessage(NetSystemType.ConnectResponse);

            if (conn.LocalHailData != null)
            {
                response.m_data.Write(conn.LocalHailData);
            }
            conn.m_unsentMessages.Enqueue(response);

            conn.m_handshakeInitiated = now;

            conn.m_approved = true;
            lock (m_connections)
                m_connections.Add(conn);
            m_connectionLookup[conn.m_remoteEndPoint] = conn;
        }
Exemple #4
0
        internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType)
        {
            OutgoingNetMessage msg = CreateOutgoingMessage();

            msg.m_type            = NetMessageLibraryType.System;
            msg.m_sequenceChannel = NetChannel.Unreliable;
            msg.m_sequenceNumber  = 0;
            msg.m_data.Write((byte)systemType);
            return(msg);
        }
Exemple #5
0
        /// <summary>
        /// Creates an outgoing net message
        /// </summary>
        internal OutgoingNetMessage CreateOutgoingMessage()
        {
            // no recycling for messages
            OutgoingNetMessage msg = new OutgoingNetMessage();

            msg.m_sequenceNumber = -1;
            msg.m_numSent        = 0;
            msg.m_nextResend     = double.MaxValue;
            msg.m_msgType        = NetMessageType.Data;
            msg.m_data           = CreateBuffer();
            return(msg);
        }
        private void AssignSequenceNumber(OutgoingNetMessage msg)
        {
            int idx = (int)msg.m_sequenceChannel;
            int nr  = m_nextSequenceToSend[idx];

            msg.m_sequenceNumber = nr;
            nr++;
            if (nr >= NetConstants.NumSequenceNumbers)
            {
                nr = 0;
            }
            m_nextSequenceToSend[idx] = nr;
        }
        internal void Connect()
        {
            m_isInitiator            = true;
            m_handshakeInitiated     = NetTime.Now;
            m_futureClose            = double.MaxValue;
            m_futureDisconnectReason = null;

            m_owner.LogVerbose("Sending Connect request to " + m_remoteEndPoint, this);
            OutgoingNetMessage msg = m_owner.CreateSystemMessage(NetSystemType.Connect);

            msg.m_data.Write(m_owner.Configuration.ApplicationIdentifier);
            msg.m_data.Write(m_owner.m_randomIdentifier);
            if (m_localHailData != null && m_localHailData.Length > 0)
            {
                msg.m_data.Write(m_localHailData);
            }
            m_unsentMessages.Enqueue(msg);
            SetStatus(NetConnectionStatus.Connecting, "Connecting");
        }
        /// <summary>
        /// Create ack message(s) for sending
        /// </summary>
        private void CreateAckMessages()
        {
            int mtuBits = ((m_owner.m_config.m_maximumTransmissionUnit - 12) / 3) * 8;

            OutgoingNetMessage ackMsg = null;
            int numAcks = m_acknowledgesToSend.Count;

            for (int i = 0; i < numAcks; i++)
            {
                if (ackMsg == null)
                {
                    ackMsg = m_owner.CreateOutgoingMessage();
                    ackMsg.m_sequenceChannel = NetChannel.Unreliable;
                    ackMsg.m_type            = NetMessageLibraryType.Acknowledge;
                }

                int ack = m_acknowledgesToSend.Dequeue();

                ackMsg.m_data.Write((byte)((ack >> 16) & 255));
                ackMsg.m_data.Write((byte)(ack & 255));
                ackMsg.m_data.Write((byte)((ack >> 8) & 255));

                //NetChannel ac = (NetChannel)(ack >> 16);
                //int asn = ack & ushort.MaxValue;
                //LogVerbose("Sending ack " + ac + "|" + asn);

                if (ackMsg.m_data.LengthBits >= mtuBits && m_acknowledgesToSend.Count > 0)
                {
                    // send and begin again
                    m_unsentMessages.Enqueue(ackMsg);
                    ackMsg = null;
                }
            }

            if (ackMsg != null)
            {
                m_unsentMessages.EnqueueFirst(ackMsg);                 // push acks to front of queue
            }
            m_statistics.CountAcknowledgesSent(numAcks);
        }
        /*
         * internal void HandleUserMessage(NetMessage msg)
         * {
         *      int seqNr = msg.m_sequenceNumber;
         *      int chanNr = (int)msg.m_sequenceChannel;
         *      bool isDuplicate = false;
         *
         *      int relation = RelateToExpected(seqNr, chanNr, out isDuplicate);
         *
         *      //
         *      // Unreliable
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.Unreliable)
         *      {
         *              // It's all good; add message
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *              }
         *              else
         *              {
         *                      AcceptMessage(msg);
         *              }
         *              return;
         *      }
         *
         *      //
         *      // Reliable unordered
         *      //
         *      if (msg.m_sequenceChannel == NetChannel.ReliableUnordered)
         *      {
         *              // send acknowledge (even if duplicate)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (isDuplicate)
         *              {
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      m_owner.LogVerbose("Rejecting duplicate " + msg, this);
         *                      return; // reject duplicates
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              return;
         *      }
         *
         *      ushort nextSeq = (ushort)(seqNr + 1);
         *
         *      if (chanNr < (int)NetChannel.ReliableInOrder1)
         *      {
         *              //
         *              // Sequenced
         *              //
         *              if (relation < 0)
         *              {
         *                      // late sequenced message
         *                      m_statistics.CountDroppedSequencedMessage();
         *                      m_owner.LogVerbose("Dropping late sequenced " + msg, this);
         *                      return;
         *              }
         *
         *              // It's good; add message
         *              AcceptMessage(msg);
         *
         *              m_nextExpectedSequence[chanNr] = nextSeq;
         *              return;
         *      }
         *      else
         *      {
         *              //
         *              // Ordered
         *              //
         *
         *              // send ack (regardless)
         *              m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber);
         *
         *              if (relation < 0)
         *              {
         *                      // late ordered message
         #if DEBUG
         *                      if (!isDuplicate)
         *                              m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this);
         #endif
         *                      // must be duplicate
         *                      m_owner.LogVerbose("Dropping duplicate message " + seqNr, this);
         *                      m_statistics.CountDuplicateMessage(msg);
         *                      return; // rejected; don't advance next expected
         *              }
         *
         *              if (relation > 0)
         *              {
         *                      // early message; withhold ordered
         *                      m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this);
         *                      m_withheldMessages.Add(msg);
         *                      return; // return without advancing next expected
         *              }
         *
         *              // It's right on time!
         *              AcceptMessage(msg);
         *
         *              // ordered; release other withheld messages?
         *              bool released = false;
         *              do
         *              {
         *                      released = false;
         *                      foreach (NetMessage wm in m_withheldMessages)
         *                      {
         *                              if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq)
         *                              {
         *                                      m_owner.LogVerbose("Releasing withheld message " + wm, this);
         *                                      m_withheldMessages.Remove(wm);
         *                                      AcceptMessage(wm);
         *                                      // no need to set rounds for this message; it was one when first related() and withheld
         *                                      nextSeq++;
         *                                      if (nextSeq >= NetConstants.NumSequenceNumbers)
         *                                              nextSeq -= NetConstants.NumSequenceNumbers;
         *                                      released = true;
         *                                      break;
         *                              }
         *                      }
         *              } while (released);
         *      }
         *
         *      // Common to Sequenced and Ordered
         *
         *      //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq);
         *      m_nextExpectedSequence[chanNr] = nextSeq;
         *
         *      return;
         * }
         */

        internal void HandleSystemMessage(IncomingNetMessage msg, double now)
        {
            msg.m_data.Position = 0;
            NetSystemType      sysType  = (NetSystemType)msg.m_data.ReadByte();
            OutgoingNetMessage response = null;

            switch (sysType)
            {
            case NetSystemType.Disconnect:
                if (m_status == NetConnectionStatus.Disconnected)
                {
                    return;
                }
                Disconnect(msg.m_data.ReadString(), 0.75f + ((float)m_currentAvgRoundtrip * 4), false, false);
                break;

            case NetSystemType.ConnectionRejected:
                string reason = msg.m_data.ReadString();
                m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint);
                Disconnect(reason, 0.0f, false, true);
                break;

            case NetSystemType.Connect:

                // ConnectReponse must have been losts

                string appIdent = msg.m_data.ReadString();
                if (appIdent != m_owner.m_config.ApplicationIdentifier)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read random identifer
                byte[] rnd = msg.m_data.ReadBytes(8);
                if (NetUtility.CompareElements(rnd, m_owner.m_randomIdentifier))
                {
                    // don't allow self-connect
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected)
                    {
                        m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read hail data
                m_remoteHailData = null;
                int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                if (hailBytesCount > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount);
                }

                // finalize disconnect if it's in process
                if (m_status == NetConnectionStatus.Disconnecting)
                {
                    FinalizeDisconnect();
                }

                // send response; even if connected
                response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse);
                if (m_localHailData != null)
                {
                    response.m_data.Write(m_localHailData);
                }
                m_unsentMessages.Enqueue(response);

                break;

            case NetSystemType.ConnectResponse:
                if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected)
                {
                    m_owner.LogWrite("Received connection response but we're not connecting...", this);
                    return;
                }

                // read hail data
                m_remoteHailData = null;
                int numHailBytes = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                if (numHailBytes > 0)
                {
                    m_remoteHailData = msg.m_data.ReadBytes(numHailBytes);
                }

                // Send connectionestablished
                response = m_owner.CreateSystemMessage(NetSystemType.ConnectionEstablished);
                if (m_localHailData != null)
                {
                    response.m_data.Write(m_localHailData);
                }
                m_unsentMessages.Enqueue(response);

                // send first ping 250ms after connected
                m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialAveragePing(now - m_handshakeInitiated);
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;

            case NetSystemType.ConnectionEstablished:
                if (m_status != NetConnectionStatus.Connecting)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Received connection response but we're not connecting...", this, msg.m_senderEndPoint);
                    }
                    return;
                }

                // read hail data
                if (m_remoteHailData == null)
                {
                    int hbc = (msg.m_data.LengthBits - msg.m_data.Position) / 8;
                    if (hbc > 0)
                    {
                        m_remoteHailData = msg.m_data.ReadBytes(hbc);
                    }
                }

                // send first ping 100-350ms after connected
                m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f);
                m_statistics.Reset();
                SetInitialAveragePing(now - m_handshakeInitiated);
                SetStatus(NetConnectionStatus.Connected, "Connected");
                break;

            case NetSystemType.Ping:
                // also accepted as ConnectionEstablished
                if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting)
                {
                    m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this);
                    m_statistics.Reset();
                    SetInitialAveragePing(now - m_handshakeInitiated);
                    SetStatus(NetConnectionStatus.Connected, "Connected");
                }

                //LogWrite("Received ping; sending pong...");
                SendPong(m_owner, m_remoteEndPoint, now);
                break;

            case NetSystemType.Pong:
                double twoWayLatency = now - m_lastSentPing;
                if (twoWayLatency < 0)
                {
                    break;
                }
                ReceivedPong(twoWayLatency, msg);
                break;

            case NetSystemType.StringTableAck:
                ushort val = msg.m_data.ReadUInt16();
                StringTableAcknowledgeReceived(val);
                break;

            default:
                m_owner.LogWrite("Undefined behaviour in NetConnection for system message " + sysType, this);
                break;
            }
        }
        internal void SendUnsentMessages(double now)
        {
            // Add any acknowledges to unsent messages
            if (m_acknowledgesToSend.Count > 0)
            {
                if (m_unsentMessages.Count < 1)
                {
                    // Wait before sending acknowledges?
                    if (m_ackMaxDelayTime > 0.0f)
                    {
                        if (m_ackWithholdingStarted == 0.0)
                        {
                            m_ackWithholdingStarted = now;
                        }
                        else
                        {
                            if (now - m_ackWithholdingStarted < m_ackMaxDelayTime)
                            {
                                return;                                 // don't send (only) acks just yet
                            }
                            // send acks "explicitly" ie. without any other message being sent
                            m_ackWithholdingStarted = 0.0;
                        }
                    }
                }

                // create ack messages and add to m_unsentMessages
                CreateAckMessages();
            }

            if (m_unsentMessages.Count < 1)
            {
                return;
            }

            // throttling
            float throttle     = m_owner.m_config.ThrottleBytesPerSecond;
            float maxSendBytes = float.MaxValue;

            if (throttle > 0)
            {
                double frameLength = now - m_lastSentUnsentMessages;

                //int wasDebt = (int)m_throttleDebt;
                if (m_throttleDebt > 0)
                {
                    m_throttleDebt -= (float)(frameLength * (double)m_owner.m_config.ThrottleBytesPerSecond);
                }
                //int nowDebt = (int)m_throttleDebt;
                //if (nowDebt != wasDebt)
                //	LogWrite("THROTTLE worked off -" + (nowDebt - wasDebt) + " bytes = " + m_throttleDebt);

                m_lastSentUnsentMessages = now;

                maxSendBytes = throttle - m_throttleDebt;
                if (maxSendBytes < 0)
                {
                    return;                     // throttling; no bytes allowed to be sent
                }
            }

            int  mtu           = m_owner.Configuration.MaximumTransmissionUnit;
            bool useCoalescing = m_owner.Configuration.UseMessageCoalescing;

            int       messagesInPacket = 0;
            NetBuffer sendBuffer       = m_owner.m_sendBuffer;

            sendBuffer.Reset();
            while (m_unsentMessages.Count > 0)
            {
                OutgoingNetMessage msg   = m_unsentMessages.Peek();
                int estimatedMessageSize = msg.m_data.LengthBytes + 5;

                // check if this message fits the throttle window
                if (estimatedMessageSize > maxSendBytes)                 // TODO: Allow at last one message if no debt
                {
                    break;
                }

                // need to send packet and start a new one?
                if (messagesInPacket > 0)
                {
                    if (!useCoalescing || (sendBuffer.LengthBytes + estimatedMessageSize > mtu))
                    {
                        m_owner.SendPacket(m_remoteEndPoint);
                        int sendLen = sendBuffer.LengthBytes;
                        m_statistics.CountPacketSent(sendLen);
                        //LogWrite("THROTTLE Send packet +" + sendLen + " bytes = " + m_throttleDebt + " (maxSendBytes " + maxSendBytes + " estimated " + estimatedMessageSize + ")");
                        m_throttleDebt += sendLen;
                        sendBuffer.Reset();
                    }
                }

                if (msg.m_sequenceNumber == -1)
                {
                    AssignSequenceNumber(msg);
                }

                // pop and encode message
                m_unsentMessages.Dequeue();
                int pre = sendBuffer.m_bitLength;
                msg.m_data.m_readPosition = 0;
                msg.Encode(sendBuffer);

                int encLen = (sendBuffer.m_bitLength - pre) / 8;
                m_statistics.CountMessageSent(msg, encLen);
                maxSendBytes -= encLen;

                if (msg.m_sequenceChannel >= NetChannel.ReliableUnordered)
                {
                    // reliable; store message (incl. buffer)
                    msg.m_numSent++;
                    StoreMessage(now, msg);
                }
                else
                {
                    // not reliable, don't store - recycle...
                    NetBuffer b = msg.m_data;
                    b.m_refCount--;

                    msg.m_data = null;

                    // ... unless someone else is using the buffer
                    if (b.m_refCount <= 0)
                    {
                        m_owner.RecycleBuffer(b);
                    }

                    //m_owner.m_messagePool.Push(msg);
                }
                messagesInPacket++;
            }

            // send current packet
            if (messagesInPacket > 0)
            {
                m_owner.SendPacket(m_remoteEndPoint);
                int sendLen = sendBuffer.LengthBytes;
                m_statistics.CountPacketSent(sendLen);
                //LogWrite("THROTTLE Send packet +" + sendLen + " bytes = " + m_throttleDebt);
                m_throttleDebt += sendLen;
            }
        }
        internal void Heartbeat(double now)
        {
            if (m_status == NetConnectionStatus.Disconnected)
            {
                return;
            }

            //CongestionHeartbeat(now);

            // drain messages from application into main unsent list
            lock (m_lockedUnsentMessages)
            {
                OutgoingNetMessage lm;
                while ((lm = m_lockedUnsentMessages.Dequeue()) != null)
                {
                    m_unsentMessages.Enqueue(lm);
                }
            }

            if (m_status == NetConnectionStatus.Connecting)
            {
                if (now - m_handshakeInitiated > m_owner.Configuration.HandshakeAttemptRepeatDelay)
                {
                    if (m_handshakeAttempts >= m_owner.Configuration.HandshakeAttemptsMaxCount)
                    {
                        Disconnect("No answer from remote host", 0, false, true);
                        return;
                    }
                    m_handshakeAttempts++;
                    if (m_isInitiator)
                    {
                        m_owner.LogWrite("Re-sending Connect", this);
                        Connect();
                    }
                    else
                    {
                        m_owner.LogWrite("Re-sending ConnectResponse", this);
                        m_handshakeInitiated = now;

                        OutgoingNetMessage response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse);
                        if (m_localHailData != null)
                        {
                            response.m_data.Write(m_localHailData);
                        }
                        m_unsentMessages.Enqueue(response);
                    }
                }
            }
            else if (m_status == NetConnectionStatus.Connected)
            {
                // send ping?
                CheckPing(now);
            }

            if (m_requestDisconnect)
            {
                InitiateDisconnect();
            }

            if (now > m_futureClose)
            {
                FinalizeDisconnect();
            }

            // Resend all packets that has reached a mature age
            ResendMessages(now);

            // send all unsent messages
            SendUnsentMessages(now);
        }
        internal void HandleAckMessage(double now, IncomingNetMessage ackMessage)
        {
            int len = ackMessage.m_data.LengthBytes;

            if ((len % 3) != 0)
            {
                if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                {
                    m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; length must be multiple of 3; it's " + len, this, ackMessage.m_senderEndPoint);
                }
                return;
            }

            for (int i = 0; i < len; i += 3)             //for each channel + seq nbr in ACK
            {
                NetChannel chan  = (NetChannel)ackMessage.m_data.ReadByte();
                int        seqNr = ackMessage.m_data.ReadUInt16();

                // LogWrite("Acknowledgement received: " + chan + "|" + seqNr);
                m_statistics.CountAcknowledgesReceived(1);

                // remove saved message
                int relChanNr = (int)chan - (int)NetChannel.ReliableUnordered;
                if (relChanNr < 0)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; indicated netchannel " + chan, this, ackMessage.m_senderEndPoint);
                    }
                    continue;
                }

                List <OutgoingNetMessage> list = m_storedMessages[relChanNr];
                if (list != null)
                {
                    int cnt = list.Count;
                    if (cnt > 0)
                    {
                        for (int j = 0; j < cnt; j++)                         //for each stored message on channel
                        {
                            OutgoingNetMessage msg = list[j];
                            if (msg.m_sequenceNumber == seqNr)                             //find correct message
                            {
                                // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Ack for " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray()));

                                //LogWrite("Removed stored message: " + msg);
                                list.RemoveAt(j);

                                // reduce estimated amount of packets on wire
                                //CongestionCountAck(msg.m_packetNumber);

                                // fire receipt
                                if (msg.m_receiptData != null)
                                {
                                    m_owner.LogVerbose("Got ack, removed from storage: " + msg + " firing receipt; " + msg.m_receiptData, this);
                                    m_owner.FireReceipt(this, msg.m_receiptData);
                                }
                                else
                                {
                                    m_owner.LogVerbose("Got ack, removed from storage: " + msg, this);
                                }

                                // recycle
                                msg.m_data.m_refCount--;
                                if (msg.m_data.m_refCount <= 0)
                                {
                                    m_owner.RecycleBuffer(msg.m_data);                                     // time to recycle buffer
                                }
                                msg.m_data = null;
                                //m_owner.m_messagePool.Push(msg);

#if !NO_NAK
                                if (j > 0)
                                {
                                    int k;
                                    for (k = 0; k < j; k++)                                     //for each message stored prior to the one matching seq nbr
                                    {
                                        var m = list[k];
                                        if (m.m_sequenceNumber > seqNr)
                                        {
                                            break;
                                        }

                                        // Re-enqueue message in unsent list
                                        m_owner.LogVerbose("Implicit NAK Resending " + m +
                                                           " now: " + NetTime.NowInMillis +
                                                           " nextResend: " + NetTime.ToMillis(m.m_nextResend), this);
                                        m_statistics.CountMessageResent(m.m_type);
                                        m_unsentMessages.Enqueue(m);
                                    }

                                    list.RemoveRange(0, k);
                                }
#endif

                                break;                                 //exit stored message loop since this was the message corresponding to seq nbr
                                //now returning to next sequence number in ACK packet
                            }
                        }
                    }
                }
            }

            // recycle
            NetBuffer rb = ackMessage.m_data;
            rb.m_refCount     = 0;         // ack messages can't be used by more than one message
            ackMessage.m_data = null;

            m_owner.RecycleBuffer(rb);
            //m_owner.m_messagePool.Push(ackMessage);
        }
Exemple #13
0
 /// <summary>
 /// Creates an outgoing net message
 /// </summary>
 internal OutgoingNetMessage CreateOutgoingMessage()
 {
     // no recycling for messages
     OutgoingNetMessage msg = new OutgoingNetMessage();
     msg.m_sequenceNumber = -1;
     msg.m_numSent = 0;
     msg.m_nextResend = double.MaxValue;
     msg.m_msgType = NetMessageType.Data;
     msg.m_data = CreateBuffer();
     return msg;
 }
Exemple #14
0
        internal void HandleAckMessage(IncomingNetMessage ackMessage)
        {
            int len = ackMessage.m_data.LengthBytes;

            if ((len % 3) != 0)
            {
                if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                {
                    m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; length must be multiple of 3; it's " + len, this, ackMessage.m_senderEndPoint);
                }
                return;
            }

            for (int i = 0; i < len; i += 3)
            {
                NetChannel chan  = (NetChannel)ackMessage.m_data.ReadByte();
                int        seqNr = ackMessage.m_data.ReadUInt16();

                // LogWrite("Acknowledgement received: " + chan + "|" + seqNr);
                m_statistics.CountAcknowledgesReceived(1);

                // remove saved message
                int chanIdx = (int)chan - (int)NetChannel.ReliableUnordered;
                if (chanIdx < 0)
                {
                    if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived)
                    {
                        m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; indicated netchannel " + chan, this, ackMessage.m_senderEndPoint);
                    }
                    continue;
                }

                List <OutgoingNetMessage> list = m_storedMessages[chanIdx];
                if (list != null)
                {
                    int cnt = list.Count;
                    if (cnt > 0)
                    {
                        for (int o = 0; o < cnt; o++)
                        {
                            OutgoingNetMessage msg = list[o];
                            if (msg.m_sequenceNumber == seqNr)
                            {
                                //LogWrite("Removed stored message: " + msg);
                                list.RemoveAt(o);

                                // reduce estimated amount of packets on wire
                                //CongestionCountAck(msg.m_packetNumber);

                                // fire receipt
                                if (msg.m_receiptData != null)
                                {
                                    m_owner.LogVerbose("Got ack, removed from storage: " + msg + " firing receipt; " + msg.m_receiptData, this);
                                    m_owner.FireReceipt(this, msg.m_receiptData);
                                }
                                else
                                {
                                    m_owner.LogVerbose("Got ack, removed from storage: " + msg, this);
                                }

                                // recycle
                                msg.m_data.m_refCount--;
                                if (msg.m_data.m_refCount <= 0)
                                {
                                    m_owner.RecycleBuffer(msg.m_data);                                     // time to recycle buffer
                                }
                                msg.m_data = null;
                                //m_owner.m_messagePool.Push(msg);

                                break;
                            }
                        }
                    }
                }
            }

            // recycle
            NetBuffer rb = ackMessage.m_data;

            rb.m_refCount     = 0;         // ack messages can't be used by more than one message
            ackMessage.m_data = null;

            m_owner.RecycleBuffer(rb);
            //m_owner.m_messagePool.Push(ackMessage);
        }
        internal void StoreMessage(double now, OutgoingNetMessage msg)
        {
            int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered;

            List<OutgoingNetMessage> list = m_storedMessages[chanBufIdx];
            if (list == null)
            {
                list = new List<OutgoingNetMessage>();
                m_storedMessages[chanBufIdx] = list;
            }
            list.Add(msg);

            // schedule resend
            float multiplier = (1 + (msg.m_numSent * msg.m_numSent)) * m_owner.m_config.m_resendTimeMultiplier;
            double nextResend = now + (0.025f + (float)m_currentAvgRoundtrip * 1.1f * multiplier);
            msg.m_nextResend = nextResend;

            m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this);

            // earliest?
            if (nextResend < m_earliestResend[chanBufIdx])
                m_earliestResend[chanBufIdx] = nextResend;
        }
 private void AssignSequenceNumber(OutgoingNetMessage msg)
 {
     int idx = (int)msg.m_sequenceChannel;
     int nr = m_nextSequenceToSend[idx];
     msg.m_sequenceNumber = nr;
     nr++;
     if (nr >= NetConstants.NumSequenceNumbers)
         nr = 0;
     m_nextSequenceToSend[idx] = nr;
 }
        // TODO: Use this with TRUE isLibraryThread for internal sendings (acks etc)

        internal void SendMessage(NetBuffer data, NetChannel channel, NetBuffer receiptData, bool isLibraryThread)
        {
            if (m_status != NetConnectionStatus.Connected)
            {
                throw new NetException("Status must be Connected to send messages");
            }

            if (data.LengthBytes > m_owner.m_config.m_maximumTransmissionUnit)
            {
                //
                // Fragmented message
                //

                int dataLen      = data.LengthBytes;
                int chunkSize    = m_owner.m_config.m_maximumTransmissionUnit - 10;              // header
                int numFragments = dataLen / chunkSize;
                if (chunkSize * numFragments < dataLen)
                {
                    numFragments++;
                }

                ushort fragId = m_nextSendFragmentId++;

                for (int i = 0; i < numFragments; i++)
                {
                    OutgoingNetMessage fmsg = m_owner.CreateOutgoingMessage();
                    fmsg.m_type    = NetMessageLibraryType.UserFragmented;
                    fmsg.m_msgType = NetMessageType.Data;

                    NetBuffer fragBuf = m_owner.CreateBuffer();
                    fragBuf.Write(fragId);
                    fragBuf.WriteVariableUInt32((uint)i);
                    fragBuf.WriteVariableUInt32((uint)numFragments);

                    if (i < numFragments - 1)
                    {
                        // normal fragment
                        fragBuf.Write(data.Data, i * chunkSize, chunkSize);
                    }
                    else
                    {
                        // last fragment
                        int bitsInLast  = data.LengthBits - (chunkSize * (numFragments - 1) * 8);
                        int bytesInLast = dataLen - (chunkSize * (numFragments - 1));
                        fragBuf.Write(data.Data, i * chunkSize, bytesInLast);

                        // add receipt only to last message
                        fmsg.m_receiptData = receiptData;
                    }
                    fmsg.m_data            = fragBuf;
                    fmsg.m_data.m_refCount = 1;                     // since it's just been created

                    fmsg.m_numSent         = 0;
                    fmsg.m_nextResend      = double.MaxValue;
                    fmsg.m_sequenceChannel = channel;
                    fmsg.m_sequenceNumber  = -1;

                    if (isLibraryThread)
                    {
                        m_unsentMessages.Enqueue(fmsg);
                    }
                    else
                    {
                        lock (m_lockedUnsentMessages)
                            m_lockedUnsentMessages.Enqueue(fmsg);
                    }
                }

                // TODO: recycle the original, unfragmented data

                return;
            }

            //
            // Normal, unfragmented, message
            //

            OutgoingNetMessage msg = m_owner.CreateOutgoingMessage();

            msg.m_msgType = NetMessageType.Data;
            msg.m_type    = NetMessageLibraryType.User;
            msg.m_data    = data;
            msg.m_data.m_refCount++;             // it could have been sent earlier also
            msg.m_numSent         = 0;
            msg.m_nextResend      = double.MaxValue;
            msg.m_sequenceChannel = channel;
            msg.m_sequenceNumber  = -1;
            msg.m_receiptData     = receiptData;

            if (isLibraryThread)
            {
                m_unsentMessages.Enqueue(msg);
            }
            else
            {
                lock (m_lockedUnsentMessages)
                    m_lockedUnsentMessages.Enqueue(msg);
            }
        }