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