bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client, bool updatewindow){ POSITION pos = waitinglist.Find(client); if (pos){ RemoveFromWaitingQueue(pos,updatewindow); return true; } else return false; }
bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client, bool updatewindow){ POSITION pos = waitinglist.Find(client); if (pos){ RemoveFromWaitingQueue(pos,updatewindow); if (updatewindow) theApp.emuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount()); client->m_bAddNextConnect = false; return true; } else return false; }
bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client) { CClientPtrList::iterator it = std::find(m_waitinglist.begin(), m_waitinglist.end(), client); if (it != m_waitinglist.end()) { RemoveFromWaitingQueue(it); Notify_ShowQueueCount(m_waitinglist.size()); return true; } else { return false; } }
/** * 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(); } }
bool CUploadQueue::AddUpNextClient(LPCTSTR pszReason, CUpDownClient* directadd){ CUpDownClient* newclient = NULL; // select next client or use given client if (!directadd) { newclient = FindBestClientInQueue(); if(newclient) { RemoveFromWaitingQueue(newclient, true); theApp.emuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount()); } } else newclient = directadd; if(newclient == NULL) return false; if (!thePrefs.TransferFullChunks()) UpdateMaxClientScore(); // refresh score caching, now that the highest score is removed if (IsDownloading(newclient)) return false; if(pszReason && thePrefs.GetLogUlDlEvents()) AddDebugLogLine(false, _T("Adding client to upload list: %s Client: %s"), pszReason, newclient->DbgGetClientInfo()); if (newclient->HasCollectionUploadSlot() && directadd == NULL){ ASSERT( false ); newclient->SetCollectionUploadSlot(false); } // tell the client that we are now ready to upload if (!newclient->socket || !newclient->socket->IsConnected()) { newclient->SetUploadState(US_CONNECTING); if (!newclient->TryToConnect(true)) return false; } else { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__AcceptUploadReq", newclient); Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0); theStats.AddUpDataOverheadFileRequest(packet->size); newclient->SendPacket(packet, true); newclient->SetUploadState(US_UPLOADING); } newclient->SetUpStartTime(); newclient->ResetSessionUp(); InsertInUploadingList(newclient); m_nLastStartUpload = ::GetTickCount(); // statistic CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)newclient->GetUploadFileID()); if (reqfile) reqfile->statistic.AddAccepted(); theApp.emuledlg->transferwnd->uploadlistctrl.AddClient(newclient); return true; }
/** * Find the highest ranking client in the waiting queue, and return it. * * Low id client are ranked as lowest possible, unless they are currently connected. * A low id client that is not connected, but would have been ranked highest if it * had been connected, gets a flag set. This flag means that the client should be * allowed to get an upload slot immediately once it connects. * * @return address of the highest ranking client. */ CUpDownClient* CUploadQueue::FindBestClientInQueue() { POSITION toadd = 0; POSITION toaddlow = 0; uint32 bestscore = 0; uint32 bestlowscore = 0; CUpDownClient* newclient = NULL; CUpDownClient* lowclient = NULL; POSITION pos1, pos2; for (pos1 = waitinglist.GetHeadPosition();( pos2 = pos1 ) != NULL;) { waitinglist.GetNext(pos1); CUpDownClient* cur_client = waitinglist.GetAt(pos2); //While we are going through this list.. Lets check if a client appears to have left the network.. ASSERT ( cur_client->GetLastUpRequest() ); if ((::GetTickCount() - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !theApp.sharedfiles->GetFileByID(cur_client->GetUploadFileID()) ) { //This client has either not been seen in a long time, or we no longer share the file he wanted anymore.. cur_client->ClearWaitStartTime(); RemoveFromWaitingQueue(pos2,true); continue; } else { // finished clearing uint32 cur_score = cur_client->GetScore(false); if ( cur_score > bestscore) { // cur_client is more worthy than current best client that is ready to go (connected). if(!cur_client->HasLowID() || (cur_client->socket && cur_client->socket->IsConnected())) { // this client is a HighID or a lowID client that is ready to go (connected) // and it is more worthy bestscore = cur_score; toadd = pos2; newclient = waitinglist.GetAt(toadd); } else if(!cur_client->m_bAddNextConnect) { // this client is a lowID client that is not ready to go (not connected) // now that we know this client is not ready to go, compare it to the best not ready client // the best not ready client may be better than the best ready client, so we need to check // against that client if (cur_score > bestlowscore) { // it is more worthy, keep it bestlowscore = cur_score; toaddlow = pos2; lowclient = waitinglist.GetAt(toaddlow); } } } } } if (bestlowscore > bestscore && lowclient) lowclient->m_bAddNextConnect = true; if (!toadd) return NULL; else return waitinglist.GetAt(toadd); }
void CUploadQueue::AddClientToUploadQueue(CUpDownClient *pClient) { EMULE_TRY CUpDownClient *pNewSource; // Select next client or use given client if (!pClient) { POSITION toadd = 0, pos1, pos2; CUpDownClient *pSource, *pLowIdClient = NULL; uint32 dwScore, dwBestScore = 0, dwBestLowIdScore = 0, dwCurTick = ::GetTickCount(); for (pos1 = waitinglist.GetHeadPosition(); (pos2 = pos1) != NULL;) { pSource = waitinglist.GetNext(pos1); // Clear dead clients ASSERT(pSource->GetLastUpRequest()); // Remove "?" from queue if ( (dwCurTick - pSource->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !g_App.m_pSharedFilesList->GetFileByID(pSource->m_reqFileHash) ) { RemoveFromWaitingQueue(pos2, true); #ifdef OLD_SOCKETS_ENABLED if (!pSource->m_pRequestSocket) { if (pSource->Disconnected()) pSource = NULL; } #endif //OLD_SOCKETS_ENABLED continue; } if ((dwScore = pSource->GetScore()) > dwBestScore) { if (!pSource->HasLowID() #ifdef OLD_SOCKETS_ENABLED || (pSource->m_pRequestSocket && pSource->m_pRequestSocket->IsConnected()) #endif //OLD_SOCKETS_ENABLED ) { // Client is a HighID or a currently connected to us LowID client dwBestScore = dwScore; toadd = pos2; } else if (!pSource->IsAddNextConnect()) { // Client is a LowID client that is not ready to go (not connected), // compare it with the best not ready client if (dwScore > dwBestLowIdScore) { dwBestLowIdScore = dwScore; pLowIdClient = waitinglist.GetAt(pos2); } } } } // The best not ready client may be better than the best ready client, // so we need to check against that client if ((dwBestLowIdScore > dwBestScore) && (pLowIdClient != NULL)) pLowIdClient->SetAddNextConnect(true); if (!toadd) return; pNewSource = waitinglist.GetAt(toadd); RemoveFromWaitingQueue(toadd, true); g_App.m_pMDlg->m_wndTransfer.UpdateUploadHeader(); } else pNewSource = pClient; if (!g_App.m_pClientList->IsValidClient(pNewSource)) return; // Never upload to already downloading client if (IsDownloading(pNewSource)) return; // Tell the client that we are now ready to upload #ifdef OLD_SOCKETS_ENABLED if (!pNewSource->IsHandshakeFinished()) { pNewSource->SetUploadState(US_CONNECTING); // Exceeding number of open TCP connections (TooManySockets) here // by one is fine, as we don't invite to upload queue too often if (!pNewSource->TryToConnect(true)) return; } else { Packet* packet = new Packet(OP_ACCEPTUPLOADREQ, 0); g_App.m_pUploadQueue->AddUpDataOverheadFileRequest(packet->m_dwSize); pNewSource->m_pRequestSocket->SendPacket(packet, true); pNewSource->SetUploadState(US_UPLOADING); pNewSource->SetLastGotULData(); } #endif //OLD_SOCKETS_ENABLED pNewSource->SetUpStartTime(); pNewSource->ResetSessionUp(); pNewSource->ResetCompressionGain(); pNewSource->SetAddNextConnect(false); EnterCriticalSection(&m_csUploadQueueList); m_UploadingList.push_back(pNewSource); LeaveCriticalSection(&m_csUploadQueueList); // clear the information about active clients, if their number in upload queue was changed m_activeClientsDeque.clear(); m_activeClientsSortedVector.clear(); // Statistic CKnownFile *pReqPartFile = g_App.m_pSharedFilesList->GetFileByID((uchar*)pNewSource->m_reqFileHash); if (pReqPartFile) pReqPartFile->statistic.AddAccepted(); g_App.m_pMDlg->m_wndTransfer.m_ctlUploadList.AddClient(pNewSource); EMULE_CATCH }
void CUploadQueue::AddUpNextClient(CUpDownClient* directadd) { CClientPtrList::iterator toadd = m_waitinglist.end(); CClientPtrList::iterator toaddlow = m_waitinglist.end(); uint32_t bestscore = 0; uint32_t bestlowscore = 0; CUpDownClient* newclient; // select next client or use given client if (!directadd) { // Track if we purged any clients from the queue, as to only send one notify in total bool purged = false; CClientPtrList::iterator it = m_waitinglist.begin(); for (; it != m_waitinglist.end(); ) { CClientPtrList::iterator tmp_it = it++; CUpDownClient* cur_client = *tmp_it; // clear dead clients if ( (::GetTickCount() - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !theApp->sharedfiles->GetFileByID(cur_client->GetUploadFileID()) ) { purged = true; cur_client->ClearWaitStartTime(); RemoveFromWaitingQueue(tmp_it); if (!cur_client->GetSocket()) { if(cur_client->Disconnected(wxT("AddUpNextClient - purged"))) { cur_client->Safe_Delete(); cur_client = NULL; } } continue; } suspendlist::iterator it2 = std::find( suspended_uploads_list.begin(), suspended_uploads_list.end(), cur_client->GetUploadFileID() ); if (cur_client->IsBanned() || it2 != suspended_uploads_list.end() ) { // Banned client or suspended upload ? continue; } // finished clearing uint32_t cur_score = cur_client->GetScore(true); if (cur_score > bestscore) { bestscore = cur_score; toadd = tmp_it; } else { cur_score = cur_client->GetScore(false); if ((cur_score > bestlowscore) && !cur_client->m_bAddNextConnect){ bestlowscore = cur_score; toaddlow = tmp_it; } } } // Update the count on GUI if any clients were purged if (purged) { Notify_ShowQueueCount(m_waitinglist.size()); } if (bestlowscore > bestscore){ newclient = *toaddlow; newclient->m_bAddNextConnect = true; } if (toadd == m_waitinglist.end()) { return; } newclient = *toadd; lastupslotHighID = true; // VQB LowID alternate RemoveFromWaitingQueue(toadd); Notify_ShowQueueCount(m_waitinglist.size()); } else { //prevent another potential access of a suspended upload suspendlist::iterator it = std::find( suspended_uploads_list.begin(), suspended_uploads_list.end(), directadd->GetUploadFileID() ); if ( it != suspended_uploads_list.end() ) { return; } else { newclient = directadd; } } if (IsDownloading(newclient)) { return; } // tell the client that we are now ready to upload if (!newclient->IsConnected()) { newclient->SetUploadState(US_CONNECTING); if (!newclient->TryToConnect(true)) { return; } } else { CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT); theStats::AddUpOverheadFileRequest(packet->GetPacketSize()); AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + newclient->GetFullIP() ); newclient->SendPacket(packet,true); newclient->SetUploadState(US_UPLOADING); } newclient->SetUpStartTime(); newclient->ResetSessionUp(); theApp->uploadBandwidthThrottler->AddToStandardList(m_uploadinglist.size(), newclient->GetSocket()); m_uploadinglist.push_back(newclient); theStats::AddUploadingClient(); // Statistic CKnownFile* reqfile = (CKnownFile*) newclient->GetUploadFile(); if (reqfile) { reqfile->statistic.AddAccepted(); } Notify_UploadCtrlAddClient(newclient); }
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()); } }