Esempio n. 1
0
bool NetBase::HandleAck(csRef<psNetPacketEntry> pkt, Connection* connection,
                        LPSOCKADDR_IN addr)
{
    psNetPacket* packet = pkt->packet;

    // REVERTED 3/8/03 Until I can figure out why this is causing client conencts to fail -  Andrew Mann (Rhad)
    // If we don't know who this connection is, don't handle ACKs
    //if (!connection)
    //    return false;


    if (packet->pktsize == PKTSIZE_ACK)  // special pktsize means ACK packet here
    {
#ifdef PACKETDEBUG
        Debug1(LOG_NET,0,"Ack received.\n");
#endif

        // receipt of ack packet is good enough to keep alive connection
        if (connection)
        {
            connection->heartbeat          = 0;
            connection->lastRecvPacketTime = csGetTicks();
            connection->pcknumin++;
        }

        csRef<psNetPacketEntry> ack;
        
        // The hash only keys on the clientnum and pktid so we need to go looking for the offset
        csArray<csRef<psNetPacketEntry> > acks = awaitingack.GetAll(PacketKey(pkt->clientnum, pkt->packet->pktid));
        for(size_t i = 0;i < acks.GetSize(); i++)
        {
            if(acks[i]->packet->offset == pkt->packet->offset)
                ack = acks[i];
        }
                

        if (ack) // if acked pkt is found, simply remove it.  We're done.
        {
            // Only update RTT estimate when packet has not been retransmitted
            if (!ack->retransmitted && connection)
            {
                csTicks elapsed = csGetTicks() - ack->timestamp;
      
                if (connection->estRTT > 0)
                {
                    int diff = (int) (elapsed - connection->estRTT);
                    connection->estRTT += (int) (0.125 * diff);
                    if(diff < 0)
                        diff = -diff;

                    connection->devRTT += (int) (0.125 * (diff - connection->devRTT));
                }
                else
                {
                    // Initialise the RTT estimates
                    connection->estRTT = elapsed;
                    connection->devRTT = elapsed / 2;
                }

                // Update the packet timeout
                connection->RTO = (int) (connection->estRTT + 4 * connection->devRTT);
                if (connection->RTO > PKTMAXRTO)
                {
                    connection->RTO = PKTMAXRTO;
                }
                else if(connection->RTO < PKTMINRTO)
                {
                    connection->RTO = PKTMINRTO;
                }

                netInfos.AddPingTicks(elapsed);
            }
            // printf ("Ping time: %i, average: %i\n", elapsed, netInfos.GetAveragePingTicks());


            if (!awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), ack))
            {
#ifdef PACKETDEBUG
                Debug2(LOG_NET,0,"No packet in ack queue :%d\n", ack->packet->pktid);
#endif
            }
            else if(connection)
            {
                connection->RemoveFromWindow(ack->packet->GetPacketSize());
            }
        }
        else // if not found, it is probably a resent ACK which is redundant so do nothing
        {
#ifdef PACKETDEBUG
            Debug1(LOG_NET,0,"No matching packet found. No problem though.\n");
#endif
        }

        return true;   // eat the packet
    }

    if (pkt->packet->GetPriority() == PRIORITY_HIGH) // a HIGH_PRIORITY packet -> ack
    {
        
#ifdef PACKETDEBUG
        Debug1(LOG_NET,0,"High priority packet received.\n");
#endif
        
        if (connection)
        {
            csRef<psNetPacketEntry> ack;
            ack.AttachNew(new psNetPacketEntry(PRIORITY_LOW,
                    pkt->clientnum,
                    pkt->packet->pktid,
                    pkt->packet->offset,
                    pkt->packet->msgsize,
                    PKTSIZE_ACK,(char *)NULL));
            
            SendFinalPacket(ack, addr);
            // ack should be unre'd here
        }
    }

    return false;
}
Esempio n. 2
0
void NetBase::CheckResendPkts()
{
    // NOTE: Globaliterators on csHash do not retrieve keys contiguously.
    csHash<csRef<psNetPacketEntry> , PacketKey>::GlobalIterator it(awaitingack.GetIterator());
    csRef<psNetPacketEntry> pkt;
    csArray<csRef<psNetPacketEntry> > pkts;
    csArray<Connection*> resentConnections;

    csTicks currenttime = csGetTicks();
    unsigned int resentCount = 0;

    while(it.HasNext())
    {
        pkt = it.Next();
        // Check the connection packet timeout
        if (pkt->timestamp + csMin((csTicks)PKTMAXRTO, pkt->RTO) < currenttime)
            pkts.Push(pkt);
    }
    for (size_t i = 0; i < pkts.GetSize(); i++)
    {
        pkt = pkts.Get(i);
#ifdef PACKETDEBUG
        Debug2(LOG_NET,0,"Resending nonacked HIGH packet (ID %d).\n", pkt->packet->pktid);
#endif
        Connection* connection = GetConnByNum(pkt->clientnum);
        if (connection)
        {
            if (resentConnections.Find(connection) == csArrayItemNotFound)
                resentConnections.Push(connection);

            // This indicates a bug in the netcode.
            if (pkt->RTO == 0)
            {
                Error1("Unexpected 0 packet RTO.");
                abort();
            }

            pkt->RTO *= 2;
            connection->resends++;
        }
        resentCount++;
        
        pkt->timestamp = currenttime;   // update stamp on packet
        pkt->retransmitted = true;
        
        // re-add to send queue
        if(NetworkQueue->Add(pkt))
        {
            //printf("pkt=%p, pkt->packet=%p\n",pkt,pkt->packet);
            // take out of awaiting ack pool.
            // This does NOT delete the pkt mem block itself.
            if (!awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt))
            {
#ifdef PACKETDEBUG
                Debug2(LOG_NET,0,"No packet in ack queue :%d\n", pkt->packet->pktid);
#endif
            }
            else if(connection)
            {
                connection->RemoveFromWindow(pkt->packet->GetPacketSize());
            }
        }
    }

    if(resentCount > 0)
    {
        resends[resendIndex] = resentCount;
        resendIndex = (resendIndex + 1) % RESENDAVGCOUNT;
        
        csTicks timeTaken = csGetTicks() - currenttime;
        if(resentCount > 300 || resendIndex == 1 || timeTaken > 50)
        {
            size_t peakResend = 0;
            float resendAvg = 0.0f;
            // Calculate averages data here
            for(int i = 0; i < RESENDAVGCOUNT; i++)
            {
                resendAvg += resends[i];
                peakResend = csMax(peakResend, resends[i]);
            }
            resendAvg /= RESENDAVGCOUNT;
            csString status;
            if(timeTaken > 50)
            {
                status.Format("Resending high priority packets has taken %u time to process, for %u packets.", timeTaken, resentCount);
                CPrintf(CON_WARNING, "%s\n", (const char *) status.GetData());
            }
            status.AppendFmt("Resending non-acked packet statistics: %g average resends, peak of %zu resent packets", resendAvg, peakResend);
 
            if(LogCSV::GetSingletonPtr())
                LogCSV::GetSingleton().Write(CSV_STATUS, status);
        }
        
    }
}
Esempio n. 3
0
void NetManager::CheckResendPkts()
{
    // NOTE: Globaliterators on csHash do not retrieve keys contiguously.
    csHash<csRef<psNetPacketEntry>, PacketKey>::GlobalIterator it (awaitingack.GetIterator());
    csRef<psNetPacketEntry> pkt;
    csArray<csRef<psNetPacketEntry> > pkts;
    csArray<Connection*> resentConnections;

    // Connections that should be avoided because we know they are full
    csArray<Connection*> fullConnections;

    csTicks currenttime = csGetTicks();
    unsigned int resentCount = 0;

    while(it.HasNext())
    {
        pkt = it.Next();
        // Check the connection packet timeout
        if (pkt->timestamp + MIN(PKTMAXRTO, pkt->RTO) < currenttime)
            pkts.Push(pkt);
    }
    for (size_t i = 0; i < pkts.GetSize(); i++)
    {
#ifdef PACKETDEBUG
        Debug2(LOG_NET,"Resending nonacked HIGH packet (ID %d).\n", pkt->packet->pktid);
#endif
        pkt = pkts.Get(i);

        // re-add to send queue
        csRef<NetPacketQueueRefCount> outqueue = clients.FindQueueAny(pkt->clientnum);
        if (!outqueue)
        {
            awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt);
            continue;
        }

        Connection* connection = GetConnByNum(pkt->clientnum);
        if (connection)
        {
            if (resentConnections.Find(connection) == csArrayItemNotFound)
                resentConnections.Push(connection);
            if (fullConnections.Find(connection) != csArrayItemNotFound)
                continue;
            // This indicates a bug in the netcode.
            if (pkt->RTO == 0)
            {
                Error1("Unexpected 0 packet RTO.");
                abort();
            }
            pkt->RTO *= 2;
            connection->resends++;
        }
        resentCount++;

        pkt->timestamp = currenttime;   // update stamp on packet
        pkt->retransmitted = true;

        /*  The proper way to send a message is to add it to the queue, and then add the queue to the senders.
        *  If you do it the other way around the net thread may remove the queue from the senders before you add the packet.
        *   Yes - this has actually happened!
        */

        /* We store the result here to return as the result of this function to maintain historic functionality.
        *  In actuality a false response does not actually mean no data was added to the queue, just that
        *  not all of the data could be added.
        */
        if (!outqueue->Add(pkt))
        {
            psNetPacket* packet = pkt->packet;
            int type = 0;

            if (packet->offset == 0)
            {
                psMessageBytes* msg = (psMessageBytes*) packet->data;
                type = msg->type;
            }
            Error4("Queue full. Could not add packet with clientnum %d type %s ID %d.\n", pkt->clientnum, type == 0 ? "Fragment" : (const char *)  GetMsgTypeName(type), pkt->packet->pktid);
            fullConnections.Push(connection);
            continue;
        }

        /**
        * The senders list is a list of busy queues.  The SendOut() function
        * in NetBase clears this list each time through.  This saves having
        * to check every single connection each time through.
        */

        if (!senders.Add (outqueue))
            Error1("Senderlist Full!");

        //printf("pkt=%p, pkt->packet=%p\n",pkt,pkt->packet);
        // take out of awaiting ack pool.
        // This does NOT delete the pkt mem block itself.
        if (!awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt))
        {
#ifdef PACKETDEBUG
            Debug2(LOG_NET,"No packet in ack queue :%d\n", pkt->packet->pktid);
#endif
        }
        else if(connection)
            connection->RemoveFromWindow(pkt->packet->GetPacketSize());
    }
    if(resentCount > 0)
    {
        resends[resendIndex] = resentCount;
        resendIndex = (resendIndex + 1) % RESENDAVGCOUNT;

        csTicks timeTaken = csGetTicks() - currenttime;
        if(resendIndex == 1 || timeTaken > 50)
        {
            unsigned int peakResend = 0;
            float resendAvg = 0.0f;
            // Calculate averages data here
            for(int i = 0; i < RESENDAVGCOUNT; i++)
            {
                resendAvg += resends[i];
                peakResend = MAX(peakResend, resends[i]);
            }
            resendAvg /= RESENDAVGCOUNT;
            csString status;
            if(timeTaken > 50 || pkts.GetSize() > 300)
            {
                status.Format("Resending high priority packets has taken %u time to process, for %u packets on %zu unique connections %zu full connections (Sample clientnum %u/RTO %u. ", timeTaken, resentCount, resentConnections.GetSize(),fullConnections.GetSize(), resentConnections[0]->clientnum, resentConnections[0]->RTO);
                CPrintf(CON_WARNING, "%s\n", (const char *) status.GetData());
            }
            status.AppendFmt("Resending non-acked packet statistics: %g average resends, peak of %u resent packets", resendAvg, peakResend);

            if(LogCSV::GetSingletonPtr())
                LogCSV::GetSingleton().Write(CSV_STATUS, status);
        }

    }

}