/** * Maintenance method for the uploading slots. It adds and removes clients to the * uploading list. It also makes sure that all the uploading slots' Sockets always have * enough packets in their queues, etc. * * This method is called approximately once every 100 milliseconds. */ void CUploadQueue::Process() { DWORD curTick = ::GetTickCount(); UpdateActiveClientsInfo(curTick); if (ForceNewClient()){ // There's not enough open uploads. Open another one. AddUpNextClient(_T("Not enough open upload slots for current ul speed")); } // The loop that feeds the upload slots with data. POSITION pos = uploadinglist.GetHeadPosition(); while(pos != NULL){ // Get the client. Note! Also updates pos as a side effect. CUpDownClient* cur_client = uploadinglist.GetNext(pos); if (thePrefs.m_iDbgHeap >= 2) ASSERT_VALID(cur_client); //It seems chatting or friend slots can get stuck at times in upload.. This needs looked into.. if (!cur_client->socket) { RemoveFromUploadQueue(cur_client, _T("Uploading to client without socket? (CUploadQueue::Process)")); if(cur_client->Disconnected(_T("CUploadQueue::Process"))){ delete cur_client; } } else { cur_client->SendBlockData(); } } // Save used bandwidth for speed calculations uint64 sentBytes = theApp.uploadBandwidthThrottler->GetNumberOfSentBytesSinceLastCallAndReset(); avarage_dr_list.AddTail(sentBytes); m_avarage_dr_sum += sentBytes; (void)theApp.uploadBandwidthThrottler->GetNumberOfSentBytesOverheadSinceLastCallAndReset(); avarage_friend_dr_list.AddTail(theStats.sessionSentBytesToFriend); // Save time beetween each speed snapshot avarage_tick_list.AddTail(curTick); // don't save more than 30 secs of data while(avarage_tick_list.GetCount() > 3 && !avarage_friend_dr_list.IsEmpty() && ::GetTickCount()-avarage_tick_list.GetHead() > 30*1000) { m_avarage_dr_sum -= avarage_dr_list.RemoveHead(); avarage_friend_dr_list.RemoveHead(); avarage_tick_list.RemoveHead(); } };
void CUploadQueue::Process() { // Check if someone's waiting, if there is a slot for him, // or if we should try to free a slot for him uint32 tick = GetTickCount(); // Nobody waiting or upload started recently // (Actually instead of "empty" it should check for "no HighID clients queued", // but the cost for that outweights the benefit. As it is, a slot will be freed // even if it can't be taken because all of the queue is LowID. But just one, // and the kicked client will instantly get it back if he has HighID.) if (m_waitinglist.empty() || tick - m_nLastStartUpload < 1000) { m_allowKicking = false; // Already a slot free, try to fill it } else if (m_uploadinglist.size() < GetMaxSlots()) { m_allowKicking = false; m_nLastStartUpload = tick; AddUpNextClient(); // All slots taken, try to free one } else { m_allowKicking = true; } // The loop that feeds the upload slots with data. CClientPtrList::iterator it = m_uploadinglist.begin(); while (it != m_uploadinglist.end()) { // Get the client. Note! Also updates pos as a side effect. CUpDownClient* cur_client = *it++; // It seems chatting or friend slots can get stuck at times in upload.. This needs looked into.. if (!cur_client->GetSocket()) { RemoveFromUploadQueue(cur_client); if(cur_client->Disconnected(_T("CUploadQueue::Process"))){ cur_client->Safe_Delete(); } } else { cur_client->SendBlockData(); } } // Save used bandwidth for speed calculations uint64 sentBytes = theApp->uploadBandwidthThrottler->GetNumberOfSentBytesSinceLastCallAndReset(); (void)theApp->uploadBandwidthThrottler->GetNumberOfSentBytesOverheadSinceLastCallAndReset(); // Update statistics if (sentBytes) { theStats::AddSentBytes(sentBytes); } }
/** * Add a client to the waiting queue for uploads. * * @param client address of the client that should be added to the waiting queue * * @param bIgnoreTimelimit don't check time limit to possibly ban the client. */ void CUploadQueue::AddClientToQueue(CUpDownClient* client, bool bIgnoreTimelimit) { //This is to keep users from abusing the limits we put on lowID callbacks. //1)Check if we are connected to any network and that we are a lowID. //(Although this check shouldn't matter as they wouldn't have found us.. // But, maybe I'm missing something, so it's best to check as a precaution.) //2)Check if the user is connected to Kad. We do allow all Kad Callbacks. //3)Check if the user is in our download list or a friend.. //We give these users a special pass as they are helping us.. //4)Are we connected to a server? If we are, is the user on the same server? //TCP lowID callbacks are also allowed.. //5)If the queue is very short, allow anyone in as we want to make sure //our upload is always used. if (theApp.IsConnected() && theApp.IsFirewalled() && !client->GetKadPort() && client->GetDownloadState() == DS_NONE && !client->IsFriend() && theApp.serverconnect && !theApp.serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort()) && GetWaitingUserCount() > 50) return; client->AddAskedCount(); client->SetLastUpRequest(); if (!bIgnoreTimelimit) client->AddRequestCount(client->GetUploadFileID()); if (client->IsBanned()) return; uint16 cSameIP = 0; // check for double POSITION pos1, pos2; for (pos1 = waitinglist.GetHeadPosition();( pos2 = pos1 ) != NULL;) { waitinglist.GetNext(pos1); CUpDownClient* cur_client= waitinglist.GetAt(pos2); if (cur_client == client) { if (client->m_bAddNextConnect && AcceptNewClient(client->m_bAddNextConnect)) { //Special care is given to lowID clients that missed their upload slot //due to the saving bandwidth on callbacks. if(thePrefs.GetLogUlDlEvents()) AddDebugLogLine(true, _T("Adding ****lowid when reconnecting. Client: %s"), client->DbgGetClientInfo()); client->m_bAddNextConnect = false; RemoveFromWaitingQueue(client, true); // statistic values // TODO: Maybe we should change this to count each request for a file only once and ignore reasks CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)client->GetUploadFileID()); if (reqfile) reqfile->statistic.AddRequest(); AddUpNextClient(_T("Adding ****lowid when reconnecting."), client); return; } client->SendRankingInfo(); theApp.emuledlg->transferwnd->queuelistctrl.RefreshClient(client); return; } else if ( client->Compare(cur_client) ) { theApp.clientlist->AddTrackClient(client); // in any case keep track of this client // another client with same ip:port or hash // this happens only in rare cases, because same userhash / ip:ports are assigned to the right client on connecting in most cases if (cur_client->credits != NULL && cur_client->credits->GetCurrentIdentState(cur_client->GetIP()) == IS_IDENTIFIED) { //cur_client has a valid secure hash, don't remove him if (thePrefs.GetVerbose()) AddDebugLogLine(false, GetResString(IDS_SAMEUSERHASH), client->GetUserName(), cur_client->GetUserName(), client->GetUserName()); return; } if (client->credits != NULL && client->credits->GetCurrentIdentState(client->GetIP()) == IS_IDENTIFIED) { //client has a valid secure hash, add him remove other one if (thePrefs.GetVerbose()) AddDebugLogLine(false, GetResString(IDS_SAMEUSERHASH), client->GetUserName(), cur_client->GetUserName(), cur_client->GetUserName()); RemoveFromWaitingQueue(pos2,true); if (!cur_client->socket) { if(cur_client->Disconnected(_T("AddClientToQueue - same userhash 1"))) delete cur_client; } } else { // remove both since we do not know who the bad one is if (thePrefs.GetVerbose()) AddDebugLogLine(false, GetResString(IDS_SAMEUSERHASH), client->GetUserName() ,cur_client->GetUserName(), _T("Both")); RemoveFromWaitingQueue(pos2,true); if (!cur_client->socket) { if(cur_client->Disconnected(_T("AddClientToQueue - same userhash 2"))) delete cur_client; } return; } } else if (client->GetIP() == cur_client->GetIP()) { // same IP, different port, different userhash cSameIP++; } } if (cSameIP >= 3) { // do not accept more than 3 clients from the same IP if (thePrefs.GetVerbose()) DEBUG_ONLY( AddDebugLogLine(false,_T("%s's (%s) request to enter the queue was rejected, because of too many clients with the same IP"), client->GetUserName(), ipstr(client->GetConnectIP())) ); return; } else if (theApp.clientlist->GetClientsFromIP(client->GetIP()) >= 3) { if (thePrefs.GetVerbose()) DEBUG_ONLY( AddDebugLogLine(false,_T("%s's (%s) request to enter the queue was rejected, because of too many clients with the same IP (found in TrackedClientsList)"), client->GetUserName(), ipstr(client->GetConnectIP())) ); return; } // done // statistic values // TODO: Maybe we should change this to count each request for a file only once and ignore reasks CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)client->GetUploadFileID()); if (reqfile) reqfile->statistic.AddRequest(); // emule collection will bypass the queue if (reqfile != NULL && CCollection::HasCollectionExtention(reqfile->GetFileName()) && reqfile->GetFileSize() < (uint64)MAXPRIORITYCOLL_SIZE && !client->IsDownloading() && client->socket != NULL && client->socket->IsConnected()) { client->SetCollectionUploadSlot(true); RemoveFromWaitingQueue(client, true); AddUpNextClient(_T("Collection Priority Slot"), client); return; } else client->SetCollectionUploadSlot(false); // cap the list // the queue limit in prefs is only a soft limit. Hard limit is 25% higher, to let in powershare clients and other // high ranking clients after soft limit has been reached uint32 softQueueLimit = thePrefs.GetQueueSize(); uint32 hardQueueLimit = thePrefs.GetQueueSize() + max(thePrefs.GetQueueSize()/4, 200); // if soft queue limit has been reached, only let in high ranking clients if ((uint32)waitinglist.GetCount() >= hardQueueLimit || (uint32)waitinglist.GetCount() >= softQueueLimit && // soft queue limit is reached (client->IsFriend() && client->GetFriendSlot()) == false && // client is not a friend with friend slot client->GetCombinedFilePrioAndCredit() < GetAverageCombinedFilePrioAndCredit()) { // and client has lower credits/wants lower prio file than average client in queue // then block client from getting on queue return; } if (client->IsDownloading()) { // he's already downloading and wants probably only another file if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__AcceptUploadReq", client); Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0); theStats.AddUpDataOverheadFileRequest(packet->size); client->SendPacket(packet, true); return; } if (waitinglist.IsEmpty() && ForceNewClient(true)) { AddUpNextClient(_T("Direct add with empty queue."), client); } else { waitinglist.AddTail(client); client->SetUploadState(US_ONUPLOADQUEUE); theApp.emuledlg->transferwnd->queuelistctrl.AddClient(client,true); theApp.emuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount()); client->SendRankingInfo(); } }
void CUploadQueue::AddClientToQueue(CUpDownClient* client) { if (theApp->serverconnect->IsConnected() && theApp->serverconnect->IsLowID() && !theApp->serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort()) && client->GetDownloadState() == DS_NONE && !client->IsFriend() && theStats::GetWaitingUserCount() > 50) { // Well, all that issues finish in the same: don't allow to add to the queue return; } if ( client->IsBanned() ) { return; } client->AddAskedCount(); client->SetLastUpRequest(); // Find all clients with the same user-hash CClientList::SourceList found = theApp->clientlist->GetClientsByHash( client->GetUserHash() ); CClientList::SourceList::iterator it = found.begin(); while (it != found.end()) { CUpDownClient* cur_client = *it++; if ( IsOnUploadQueue( cur_client ) ) { if ( cur_client == client ) { // This is where LowID clients get their upload slot assigned. // They can't be contacted if they reach top of the queue, so they are just marked for uploading. // When they reconnect next time AddClientToQueue() is called, and they get their slot // through the connection they initiated. // Since at that time no slot is free they get assigned an extra slot, // so then the number of slots exceeds the configured number by one. // To prevent a further increase no more LowID clients get a slot, until // - a HighID client has got one (which happens only after two clients // have been kicked so a slot is free again) // - or there is a free slot, which means there is no HighID client on queue if (client->m_bAddNextConnect) { uint16 maxSlots = GetMaxSlots(); if (lastupslotHighID) { maxSlots++; } if (m_uploadinglist.size() < maxSlots) { client->m_bAddNextConnect = false; RemoveFromWaitingQueue(client); AddUpNextClient(client); lastupslotHighID = false; // LowID alternate return; } } client->SendRankingInfo(); Notify_QlistRefreshClient(client); return; } else { // Hash-clash, remove unidentified clients (possibly both) if ( !cur_client->IsIdentified() ) { // Cur_client isn't identifed, remove it theApp->clientlist->AddTrackClient( cur_client ); RemoveFromWaitingQueue( cur_client ); if ( !cur_client->GetSocket() ) { if (cur_client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) { cur_client->Safe_Delete(); } } } if ( !client->IsIdentified() ) { // New client isn't identified, remove it theApp->clientlist->AddTrackClient( client ); if ( !client->GetSocket() ) { if ( client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) { client->Safe_Delete(); } } return; } } } } // Count the number of clients with the same IP-address found = theApp->clientlist->GetClientsByIP( client->GetIP() ); int ipCount = 0; for ( it = found.begin(); it != found.end(); it++ ) { if ( ( *it == client ) || IsOnUploadQueue( *it ) ) { ipCount++; } } // We do not accept more than 3 clients from the same IP if ( ipCount > 3 ) { return; } else if ( theApp->clientlist->GetClientsFromIP(client->GetIP()) >= 3 ) { return; } // statistic values CKnownFile* reqfile = (CKnownFile*) client->GetUploadFile(); if (reqfile) { reqfile->statistic.AddRequest(); } // TODO find better ways to cap the list if (m_waitinglist.size() >= (thePrefs::GetQueueSize())) { return; } if (client->IsDownloading()) { // he's already downloading and wants probably only another file CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT); theStats::AddUpOverheadFileRequest(packet->GetPacketSize()); AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + client->GetFullIP() ); client->SendPacket(packet,true); return; } uint32 tick = GetTickCount(); client->ClearWaitStartTime(); // if possible start upload right away if (m_waitinglist.empty() && tick - m_nLastStartUpload >= 1000 && m_uploadinglist.size() < GetMaxSlots()) { AddUpNextClient(client); m_nLastStartUpload = tick; } else { m_waitinglist.push_back(client); theStats::AddWaitingClient(); client->ClearAskedCount(); client->SetUploadState(US_ONUPLOADQUEUE); client->SendRankingInfo(); Notify_QlistAddClient(client); Notify_ShowQueueCount(m_waitinglist.size()); } }