Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

EDClients.cpp

Go to the documentation of this file.
00001 //
00002 // EDClients.cpp
00003 //
00004 // Copyright (c) Shareaza Development Team, 2002-2005.
00005 // This file is part of SHAREAZA (www.shareaza.com)
00006 //
00007 // Shareaza is free software; you can redistribute it
00008 // and/or modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2 of
00010 // the License, or (at your option) any later version.
00011 //
00012 // Shareaza is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with Shareaza; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 
00022 #include "StdAfx.h"
00023 #include "Shareaza.h"
00024 #include "Settings.h"
00025 #include "Transfers.h"
00026 #include "EDClient.h"
00027 #include "EDClients.h"
00028 #include "EDPacket.h"
00029 
00030 #include "Network.h"
00031 #include "Security.h"
00032 #include "Datagrams.h"
00033 #include "Downloads.h"
00034 #include "QueryHit.h"
00035 #include "SearchManager.h"
00036 
00037 #include "Neighbours.h"
00038 #include "HostCache.h"
00039 
00040 #ifdef _DEBUG
00041 #undef THIS_FILE
00042 static char THIS_FILE[]=__FILE__;
00043 #define new DEBUG_NEW
00044 #endif
00045 
00046 CEDClients EDClients;
00047 
00048 
00050 // CEDClients construction
00051 
00052 CEDClients::CEDClients()
00053 {
00054         m_pFirst                        = NULL;
00055         m_pLast                         = NULL;
00056         m_nCount                        = 0;
00057         m_tLastRun                      = 0;
00058         m_tLastMaxClients       = 0;
00059         m_tLastServerStats      = 0;
00060         m_nLastServerKey        = 0;
00061         m_bAllServersDone       = FALSE;
00062 }
00063 
00064 CEDClients::~CEDClients()
00065 {
00066         Clear();
00067 }
00068 
00070 // CEDClients add and remove
00071 
00072 void CEDClients::Add(CEDClient* pClient)
00073 {
00074         ASSERT( pClient->m_pEdPrev == NULL );
00075         ASSERT( pClient->m_pEdNext == NULL );
00076         
00077         pClient->m_pEdPrev = m_pLast;
00078         pClient->m_pEdNext = NULL;
00079         
00080         if ( m_pLast != NULL )
00081         {
00082                 m_pLast->m_pEdNext = pClient;
00083                 m_pLast = pClient;
00084         }
00085         else
00086         {
00087                 m_pFirst = m_pLast = pClient;
00088         }
00089         
00090         m_nCount++;
00091 }
00092 
00093 void CEDClients::Remove(CEDClient* pClient)
00094 {
00095         ASSERT( m_nCount > 0 );
00096         
00097         if ( pClient->m_pEdPrev != NULL )
00098                 pClient->m_pEdPrev->m_pEdNext = pClient->m_pEdNext;
00099         else
00100                 m_pFirst = pClient->m_pEdNext;
00101         
00102         if ( pClient->m_pEdNext != NULL )
00103                 pClient->m_pEdNext->m_pEdPrev = pClient->m_pEdPrev;
00104         else
00105                 m_pLast = pClient->m_pEdPrev;
00106         
00107         m_nCount --;
00108 }
00109 
00111 // CEDClients clear
00112 
00113 void CEDClients::Clear()
00114 {
00115         for ( CEDClient* pClient = m_pFirst ; pClient ; )
00116         {
00117                 CEDClient* pNext = pClient->m_pEdNext;
00118                 pClient->Remove();
00119                 pClient = pNext;
00120         }
00121         
00122         ASSERT( m_pFirst == NULL );
00123         ASSERT( m_pLast == NULL );
00124         ASSERT( m_nCount == 0 );
00125 }
00126 
00128 // CEDClients push connection setup
00129 
00130 BOOL CEDClients::PushTo(DWORD nClientID, WORD nClientPort)
00131 {
00132         CEDClient* pClient = Connect( nClientID, nClientPort, NULL, 0, NULL );
00133         if ( pClient == NULL ) return FALSE;
00134         return pClient->Connect();
00135 }
00136 
00138 // CEDClients connection setup
00139 
00140 CEDClient* CEDClients::Connect(DWORD nClientID, WORD nClientPort, IN_ADDR* pServerAddress, WORD nServerPort, GGUID* pGUID)
00141 {
00142         if ( pGUID != NULL )
00143         {
00144                 if ( CEDClient* pClient = GetByGUID( pGUID ) ) return pClient;
00145         }
00146         
00147         if ( IsFull() ) return NULL;
00148         
00149         CEDClient* pClient = NULL;
00150         
00151         if ( CEDPacket::IsLowID( nClientID ) )
00152         {
00153                 if ( pServerAddress == NULL || nServerPort == 0 ) return NULL;
00154                 pClient = GetByID( nClientID, pServerAddress, pGUID );
00155         }
00156         else
00157         {
00158                 if ( Security.IsDenied( (IN_ADDR*)&nClientID ) ) return NULL;
00159                 pClient = GetByID( nClientID, NULL, pGUID );
00160         }
00161         
00162         if ( pClient == NULL )
00163         {
00164                 pClient = new CEDClient();
00165                 pClient->ConnectTo( nClientID, nClientPort, pServerAddress, nServerPort, pGUID );
00166         }
00167         
00168         return pClient;
00169 }
00170 
00172 // CEDClients find by client ID and/or GUID
00173 
00174 CEDClient* CEDClients::GetByIP(IN_ADDR* pAddress)
00175 {
00176         for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00177         {
00178                 if ( pClient->m_pHost.sin_addr.S_un.S_addr == pAddress->S_un.S_addr )
00179                         return pClient;
00180         }
00181         
00182         return NULL;
00183 }
00184 
00185 CEDClient* CEDClients::GetByID(DWORD nClientID, IN_ADDR* pServer, GGUID* pGUID)
00186 {
00187         for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00188         {
00189                 if ( pServer && pClient->m_pServer.sin_addr.S_un.S_addr != pServer->S_un.S_addr ) continue;
00190                 
00191                 if ( pClient->m_nClientID == nClientID )
00192                 {
00193                         if ( pGUID == NULL || pClient->m_pGUID == *pGUID ) return pClient;
00194                 }
00195         }
00196         
00197         return NULL;
00198 }
00199 
00200 CEDClient* CEDClients::GetByGUID(GGUID* pGUID)
00201 {
00202         for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00203         {
00204                 if ( pClient->m_bGUID && pClient->m_pGUID == *pGUID ) return pClient;
00205         }
00206         
00207         return NULL;
00208 }
00209 
00211 // CEDClients merge
00212 
00213 BOOL CEDClients::Merge(CEDClient* pClient)
00214 {
00215         ASSERT( pClient != NULL );
00216 
00217         for ( CEDClient* pOther = m_pFirst ; pOther ; pOther = pOther->m_pEdNext )
00218         {
00219                 if ( pOther != pClient && pOther->Equals( pClient ) )
00220                 {
00221                         pClient->Merge( pOther );
00222                         pOther->Remove();
00223                         return TRUE;
00224                 }
00225         }
00226         
00227         return FALSE;
00228 }
00229 
00231 // CEDClients full test
00232 
00233 BOOL CEDClients::IsFull(CEDClient* pCheckThis)
00234 {
00235         int nCount = 0;
00236         DWORD tNow = GetTickCount();
00237 
00238         // Count the number of connected clients
00239         for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00240         {
00241                 if ( pClient->m_hSocket != INVALID_SOCKET ) nCount++;
00242         }
00243 
00244         // If there are more clients current connected than there should be, set the full timer
00245         if ( nCount >= Settings.eDonkey.MaxLinks ) m_tLastMaxClients = tNow;
00246 
00247         // If we have not been full in the past 2 seconds, then we're okay to start new connections
00248         if ( ( tNow - m_tLastMaxClients ) > ( 2*1000 ) ) return FALSE;
00249 
00250         // If we're checking a client that's already connected, say we aren't full. (don't drop it)
00251         if ( ( pCheckThis != NULL ) && ( pCheckThis->m_hSocket != INVALID_SOCKET ) ) return FALSE;
00252         
00253 
00254         // We're too full to start new connections
00255         return TRUE;
00256 
00257 
00258 /*
00259         if ( pCheckThis != NULL )
00260         {
00261                 for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00262                 {
00263                         if ( pClient->m_hSocket != INVALID_SOCKET ) nCount++;
00264                         if ( pClient == pCheckThis ) pCheckThis = NULL;
00265                 }
00266         }
00267         else
00268         {
00269                 for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00270                 {
00271                         if ( pClient->m_hSocket != INVALID_SOCKET ) nCount++;
00272                 }
00273         }
00274         
00275         ASSERT( pCheckThis == NULL );
00276         return ( nCount >= Settings.eDonkey.MaxLinks ) || ( pCheckThis != NULL );*/
00277 }
00278 
00279 BOOL CEDClients::IsOverloaded()
00280 {
00281         int nCount = 0;
00282 
00283         for ( CEDClient* pClient = m_pFirst ; pClient ; pClient = pClient->m_pEdNext )
00284         {
00285                 if ( pClient->m_hSocket != INVALID_SOCKET ) nCount++;
00286         }
00287         
00288         return ( nCount >= ( Settings.eDonkey.MaxLinks + 25 ) );
00289 }
00290 
00291 
00293 // CEDClients run
00294 
00295 void CEDClients::OnRun()
00296 {
00297 
00298         // Delay to limit the rate of ed2k packets being sent.
00299         // keep ed2k transfers under 10 KB/s per source
00300         DWORD tNow = GetTickCount();
00301         if ( tNow - m_tLastRun < Settings.eDonkey.PacketThrottle ) return;
00302         m_tLastRun = tNow;
00303 
00304         if ( Settings.eDonkey.ServerWalk && Settings.eDonkey.EnableToday )
00305         {
00306                 RunGlobalStatsRequests( tNow );
00307         }
00308         
00309         for ( CEDClient* pClient = m_pFirst ; pClient ; )
00310         {
00311                 CEDClient* pNext = pClient->m_pEdNext;
00312                 pClient->OnRunEx( tNow );
00313                 pClient = pNext;
00314         }
00315 }
00316 
00318 // CEDClients accept new connections
00319 
00320 BOOL CEDClients::OnAccept(CConnection* pConnection)
00321 {
00322         ASSERT( pConnection != NULL );
00323         
00324         if ( Settings.Connection.RequireForTransfers && ! Settings.eDonkey.EnableToday )
00325         {
00326                 theApp.Message( MSG_ERROR, IDS_ED2K_CLIENT_DISABLED,
00327                         (LPCTSTR)pConnection->m_sAddress );
00328                 return FALSE;
00329         }
00330         
00331         CSingleLock pLock( &Transfers.m_pSection );
00332         if ( ! pLock.Lock( 250 ) ) return FALSE;
00333 
00334         if ( IsFull() )
00335         {
00336                 // Even if we're full, we still need to accept connections from clients we have queued, etc
00337                 if ( ( GetByIP( &pConnection->m_pHost.sin_addr ) == NULL ) || ( IsOverloaded() ) )
00338                 {
00339                         theApp.Message( MSG_DEBUG, _T("Rejecting ed2k connection from %s, max client connections reached."),
00340                                 (LPCTSTR)pConnection->m_sAddress );
00341                         return FALSE;
00342                 }
00343                 else 
00344                 {
00345                         theApp.Message( MSG_DEBUG, _T("Accepting ed2k connection from %s despite client connection limit."),
00346                                 (LPCTSTR)pConnection->m_sAddress );
00347 
00348                 }
00349         }
00350         
00351         CEDClient* pClient = new CEDClient();
00352         pClient->AttachTo( pConnection );
00353         
00354         return TRUE;
00355 }
00356 
00358 // CEDClients process UDP Packets
00359 
00360 // UDP packet received
00361 BOOL CEDClients::OnUDP(SOCKADDR_IN* pHost, CEDPacket* pPacket)
00362 {
00363         CSingleLock pLock( &Transfers.m_pSection );
00364         
00365         switch ( pPacket->m_nType )
00366         {
00367         case ED2K_C2C_UDP_REASKFILEPING:
00368                 if ( ! pLock.Lock( 100 ) ) return FALSE;
00369                 if ( CEDClient* pClient = GetByIP( &pHost->sin_addr ) )
00370                 {
00371                         pClient->m_nUDP = ntohs( pHost->sin_port );
00372                         
00373                         if ( ! pClient->OnUdpReask( pPacket ) )
00374                         {
00375                                 Datagrams.Send( pHost, CEDPacket::New( ED2K_C2C_UDP_FILENOTFOUND, ED2K_PROTOCOL_EMULE ) );
00376                         }
00377                 }
00378                 else
00379                 {
00380                         Datagrams.Send( pHost, CEDPacket::New( ED2K_C2C_UDP_FILENOTFOUND, ED2K_PROTOCOL_EMULE ) );
00381                 }
00382                 break;
00383         case ED2K_C2C_UDP_REASKACK:
00384                 if ( ! pLock.Lock( 100 ) ) return FALSE;
00385                 if ( CEDClient* pClient = GetByIP( &pHost->sin_addr ) )
00386                 {
00387                         pClient->m_nUDP = ntohs( pHost->sin_port );
00388                         pClient->OnUdpReaskAck( pPacket );
00389                 }
00390                 break;
00391         case ED2K_C2C_UDP_QUEUEFULL:
00392                 if ( ! pLock.Lock( 100 ) ) return FALSE;
00393                 if ( CEDClient* pClient = GetByIP( &pHost->sin_addr ) )
00394                 {
00395                         pClient->m_nUDP = ntohs( pHost->sin_port );
00396                         pClient->OnUdpQueueFull( pPacket );
00397                 }
00398                 break;
00399         case ED2K_C2C_UDP_FILENOTFOUND:
00400                 if ( ! pLock.Lock( 100 ) ) return FALSE;
00401                 if ( CEDClient* pClient = GetByIP( &pHost->sin_addr ) )
00402                 {
00403                         pClient->m_nUDP = ntohs( pHost->sin_port );
00404                         pClient->OnUdpFileNotFound( pPacket );
00405                 }
00406                 break;
00407         case ED2K_S2CG_SERVERSTATUS:
00408                 OnServerStatus( pHost, pPacket );
00409                 break;
00410         case ED2K_S2CG_SEARCHRESULT:
00411         case ED2K_S2CG_FOUNDSOURCES:
00412                 // Correct port value. (UDP port is TCP port + 4)
00413                 pHost->sin_port = htons( ntohs( pHost->sin_port ) - 4 );
00414 
00415                 // Check server details in host cache
00416                 CHostCacheHost *pServer;
00417                 DWORD nServerFlags = Settings.eDonkey.DefaultServerFlags;
00418                 pServer = HostCache.eDonkey.Find( &pHost->sin_addr );
00419                 if ( pServer && pServer->m_nUDPFlags )
00420                 {
00421                         nServerFlags = pServer->m_nUDPFlags;
00422                 }
00423 
00424                 // Decode packet and create hits
00425                 if ( CQueryHit* pHits = CQueryHit::FromPacket( pPacket, pHost, nServerFlags ) )
00426                 {
00427                         Downloads.OnQueryHits( pHits );
00428                         
00429                         if ( pPacket->m_nType == ED2K_S2CG_SEARCHRESULT )
00430                                 Network.OnQueryHits( pHits );
00431                         else
00432                                 pHits->Delete();
00433                 }
00434                 break;
00435         }
00436         
00437         return TRUE;
00438 }
00439 
00440 // Server status packet received
00441 void CEDClients::OnServerStatus(SOCKADDR_IN* pHost, CEDPacket* pPacket)
00442 {
00443         DWORD nLen, nKey;
00444         DWORD nUsers = 0, nFiles = 0, nMaxUsers = 0, nFileLimit = 1000, nUDPFlags = 0;
00445 
00446         // Read in and check the key value to make sure we requested this update
00447         nKey = pPacket->ReadLongLE();
00448         if ( nKey != m_nLastServerKey )
00449         {
00450                 theApp.Message( MSG_ERROR, _T("Received unexpected server status" ) );
00451                 return;
00452         }
00453 
00454         // Got a status update we were expecting.
00455         m_nLastServerKey = 0;
00456         CHostCacheHost *pServer = HostCache.eDonkey.Find( &m_pLastServer );
00457         if ( pServer == NULL )
00458         {
00459                 theApp.Message( MSG_DEFAULT, _T("Server status received, but server not found in host cache") );
00460                 return;
00461         }
00462         if ( pServer->m_sName.GetLength() )
00463                 theApp.Message( MSG_DEFAULT, _T("Server status received from %s"), pServer->m_sName );
00464         else
00465                 theApp.Message( MSG_DEFAULT, _T("Server status received from %s"),
00466             (LPCTSTR)CString( inet_ntoa( m_pLastServer ) ) );
00467 
00468         // Read in the status packet
00469         nLen = pPacket->GetRemaining();
00470 
00471         if ( nLen >= 8 ) 
00472         {
00473                 // Current users and files indexed
00474                 nUsers = pPacket->ReadLongLE();
00475                 nFiles = pPacket->ReadLongLE();
00476         }
00477         if ( nLen >= 12 ) 
00478         {
00479                 // Maximum users allowed
00480                 nMaxUsers = pPacket->ReadLongLE();
00481         }
00482         if ( nLen >= 20 ) 
00483         {
00484                 // Client file limit. (Maximum files you can send to the server)
00485                 nFileLimit = pPacket->ReadLongLE();     // Soft limit. (Files over this are ignored)
00486                 pPacket->ReadLongLE();                          // 'Hard' limit. (Obey previous, it saves bandwidth)
00487         }
00488         if ( nLen >= 24 ) 
00489         {
00490                 // UDP Flags. (This is important, it determines search types, etc)
00491                 nUDPFlags = pPacket->ReadLongLE();
00492         }
00493         if ( nLen >= 28 ) 
00494         {
00495                 // Low ID users. 
00496                 pPacket->ReadLongLE(); // We don't use this
00497         }
00498 
00499         // Update the server variables
00500         pServer->m_nFailures    = 0;
00501         pServer->m_tFailure             = 0;
00502         pServer->m_nUserCount   = nUsers;
00503         pServer->m_nUserLimit   = nMaxUsers;
00504         pServer->m_nFileLimit   = nFileLimit;
00505         pServer->m_nUDPFlags    = nUDPFlags;
00506 
00507         if ( pServer->m_tSeen < pServer->m_tStats ) 
00508                 pServer->m_tSeen = pServer->m_tStats;
00509         if ( nUDPFlags & ED2K_SERVER_UDP_UNICODE ) 
00510                 pServer->m_nTCPFlags |= ED2K_SERVER_TCP_UNICODE;
00511         if ( nUDPFlags & ED2K_SERVER_UDP_GETSOURCES2 ) 
00512                 pServer->m_nTCPFlags |= ED2K_SERVER_TCP_GETSOURCES2;
00513 
00514         //CString strT;
00515         //strT.Format( _T("Users:%d Files:%d Max Users:%d File limit:%d UDP flags:%08X"), nUsers, nFiles, nMaxUsers, nFileLimit, nUDPFlags );
00516         //theApp.Message( MSG_DEFAULT, strT );
00517 }
00518 
00519 // Send a server status request
00520 void CEDClients::RequestServerStatus(IN_ADDR* pHost, WORD nPort)
00521 {
00522         CEDPacket* pPacket = CEDPacket::New( ED2K_C2SG_SERVERSTATUSREQUEST, ED2K_PROTOCOL_EDONKEY );
00523 
00524         srand( GetTickCount() );
00525         m_nLastServerKey = 0x55AA0000 + rand();
00526         pPacket->WriteLongLE( m_nLastServerKey );
00527         Datagrams.Send( pHost, nPort + 4, pPacket );
00528 }
00529 
00531 // CEDClients send/time out UDP global server status packets
00532 
00533 void CEDClients::RunGlobalStatsRequests(DWORD tNow)
00534 {
00535         CHostCacheHost *pHost;
00536 
00537         // Don't send stat requests or time out servers if we're not stable
00538         if ( ! Datagrams.IsStable() ) return;
00539 
00540         if ( m_nLastServerKey != 0 )
00541         {
00542                 // We are waiting for a response
00543                 if ( tNow > m_tLastServerStats + Settings.Connection.TimeoutHandshake )
00544                 {
00545                         CSingleLock pLock( &Network.m_pSection );
00546                         if ( ! pLock.Lock( 250 ) ) return;
00547 
00548                         // Timed out
00549                         m_nLastServerKey = 0;
00550                         theApp.Message( MSG_DEBUG, _T("Time-out waiting for ed2k server status") );
00551 
00552                         pHost = HostCache.eDonkey.Find( &m_pLastServer );
00553                         if ( pHost )
00554                         {
00555                                 pHost->m_tFailure = pHost->m_tStats;
00556 
00557                                 pHost->m_nFailures ++;
00558                                 // If we've had multiple failures, remove the host
00559                                 if ( pHost->m_nFailures > 3 )
00560                                 {               
00561                                         theApp.Message( MSG_DEFAULT, _T("Removing ed2k server %s"), pHost->m_sName );
00562                                         HostCache.eDonkey.Remove( pHost );
00563                                 }
00564                         }       
00565                         // Reset the timer so we query another server right away
00566                         // m_tLastServerStats = 0;
00567                         pLock.Unlock();
00568                 }
00569         }
00570 
00571         if ( tNow > m_tLastServerStats + Settings.eDonkey.StatsGlobalThrottle ) // Limit requests to every 30 minutes
00572         {
00573                 // We are due to send another stats request
00574                 CSingleLock pLock( &Network.m_pSection );
00575                 if ( ! pLock.Lock( 250 ) ) return;
00576 
00577                 // Get the current time (in seconds)
00578                 DWORD tSecs     = time( NULL );
00579 
00580                 // Loop through servers in the host cache
00581                 for ( pHost = HostCache.eDonkey.GetNewest() ; pHost ; pHost = pHost->m_pPrevTime )
00582                 {
00583                         /*CString strT;
00584                         strT.Format( _T("  -Name:%s Last Stats:%d UDP flags:%08X"), pHost->m_sName, pHost->m_tStats, pHost->m_nUDPFlags );
00585                         theApp.Message( MSG_DEFAULT, strT );*/
00586 
00587                         // Check if this server could be asked for stats
00588                         if ( ( pHost->CanQuery( tSecs ) ) &&                                                                                            // If it hasn't been searched recently  
00589                                  ( ( tSecs > pHost->m_tStats + Settings.eDonkey.StatsServerThrottle  ) ||               // AND we have not checked this host in a week OR
00590                                    ( ( pHost->m_nFailures > 0 ) && ( tSecs > pHost->m_tStats + 8*60*60  ) ) ) &&        // -last check failed, have not checked in 8 hours
00591                                  ( ( pHost->m_nUDPFlags == 0 ) ||                                                                                               // AND it has no flags set OR
00592                                    ( m_bAllServersDone ) ) )                                                                                                            // -we have checked all servers         
00593                         {
00594                                 // Don't ask current neighbours for stats
00595                                 if ( ! Neighbours.Get( &pHost->m_pAddress ) )
00596                                 {
00597                                         // Send a request for stats to this server
00598                                         if ( pHost->m_sName.GetLength() )
00599                                                 theApp.Message( MSG_DEFAULT, _T("Sending status request to ed2k server %s"), pHost->m_sName );
00600                                         else
00601                                                 theApp.Message( MSG_DEFAULT, _T("Sending status request to ed2k server %s"), (LPCTSTR)CString( inet_ntoa( pHost->m_pAddress ) ) );
00602 
00603                                         RequestServerStatus( &pHost->m_pAddress, pHost->m_nPort );
00604                                         pHost->m_tStats = tSecs;
00605                                         m_tLastServerStats = tNow;
00606                                         m_pLastServer = pHost->m_pAddress;
00607                                         return;
00608                                 }
00609                         }
00610                 }
00611                 pLock.Unlock();
00612 
00613                 // We have checked all known servers, we may go back and re-query any that didn't respond.
00614                 m_bAllServersDone = TRUE;
00615                 // Try again later. (we don't want to keep running this section, it's a little slow)
00616                 m_tLastServerStats = tNow;
00617         }
00618 }

Generated on Thu Dec 15 10:39:41 2005 for Shareaza 2.2.1.0 by  doxygen 1.4.2