ConnectionKeeper.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko 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 Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "ConnectionKeeper.h"
00025 #include "NodeCache.h"
00026 #include "NodeCacheFilters.h"
00027 #include "UdpHostCache.h"
00028 #include "Imports.cpp"
00029 
00030 using namespace Gnutella::Bootstrapping;
00031 
00032 namespace Gnutella {
00033 namespace Bootstrapping {
00034 
00035 enum Constants
00036 {
00037     UdpPingTimeout      = 2000,
00038     UdpPingTries        = 5,
00039     UdpPingsPerTry      = 10
00040 };
00041 
00042 static const float HighUptimeThreshold  = 0.7;
00043 
00044 class ConnectionKeeperPrivate
00045 {
00046     REFERENCE_OBJECT (ConnectionKeeperPrivate)
00047 
00048 public:
00049     LocalPeer       *localPeer;
00050     UdpHostCache    *udpHostCache;
00051     SlotAllocator   *slotAllocator;
00052     NodeCache       *nodeCache;
00053     bool            isBootstrapping;
00054     QTimer          bootstrappingTimer;
00055     QDateTime       bootstrappingStart;
00056 
00057     NodeSet         getBootstrapNodes (int count);
00058     NodeSet         getConnectNodes (int count);
00059 
00060     ConnectionKeeperPrivate (LocalPeer *localPeer_)
00061      :  localPeer (localPeer_),
00062         udpHostCache (localPeer_->udpHostCache()),
00063         slotAllocator (localPeer->slotAllocator()),
00064         nodeCache (localPeer->nodeCache()),
00065         isBootstrapping (false),
00066         bootstrappingTimer(), bootstrappingStart()
00067     {}
00068 
00069 };
00070 
00071 } // namespace Bootstrapping;
00072 } // namespace Gnutella;
00073 
00076 NodeSet ConnectionKeeperPrivate::getBootstrapNodes (int count)
00077 {
00078     NodeCache::NodeAvailability availability = NodeCache::UnknownAvailability;
00079     NodeSet nodes;
00080     if (count == 0)
00081         return nodes;
00082     // Create a predicate object
00083     HighUptimeHost highUptimeHost (uint(HighUptimeThreshold *
00084                                         DailyUptime::MaximalUptime));
00085     nodes += nodeCache->getNodes (count - nodes.count(), availability, true,
00086                                   highUptimeHost);
00087     nodes += nodeCache->getNodes (count - nodes.count(), availability, true);
00088     nodes += nodeCache->getNodes (count - nodes.count(), availability, false,
00089                                   highUptimeHost);
00090     nodes += nodeCache->getNodes (count - nodes.count(), availability, false);
00091     return nodes;
00092 }
00093 
00096 NodeSet ConnectionKeeperPrivate::getConnectNodes (int count)
00097 {
00098     NodeSet nodes;
00099     NodeCache::NodeAvailability availability = NodeCache::UnknownAvailability;
00100     if (count == 0)
00101         return nodes;
00102     if (localPeer->isUltrapeer())
00103         nodes += nodeCache->getNodes (count - nodes.count(), availability,
00104                                         true, HasUltrapeerSlots());
00105     else
00106         nodes += nodeCache->getNodes (count - nodes.count(), availability,
00107                                         true, HasLeafSlots());
00108     nodes += nodeCache->getNodes (count - nodes.count(), availability, true);
00109     return nodes;
00110 }
00111 
00112 ConnectionKeeper::ConnectionKeeper (LocalPeer *localPeer)
00113  :  p (new ConnectionKeeperPrivate (localPeer))
00114 {
00115     connect (&p->bootstrappingTimer,    SIGNAL (timeout()),
00116              this,                      SLOT (bootstrap()));
00117 }
00118 
00119 ConnectionKeeper::~ConnectionKeeper()
00120 {
00121     delete p;
00122 }
00123 
00124 void ConnectionKeeper::bootstrap()
00125 {
00126     if (!p->slotAllocator->isBootstrapping()) {
00127         p->isBootstrapping = false;
00128         p->bootstrappingTimer.stop();
00129         return;
00130     }
00131 
00132     if (p->isBootstrapping == false) {
00133         p->isBootstrapping = true;
00134         p->bootstrappingStart = QDateTime::currentDateTime();
00135         p->bootstrappingTimer.start (UdpPingTimeout);
00136         // Try both over TCP and UDP. UDP might not be working for some nodes:
00137         keepConnection(); // Try TCP connections. UDP pinging is done below.
00138     }
00139 
00140     QDateTime now = QDateTime::currentDateTime();
00141     if (now < p->bootstrappingStart.addMSecs (UdpPingTries * UdpPingTimeout)) {
00142         connect (p->nodeCache,  SIGNAL (nodeAdded()),
00143                  this,          SLOT (keepConnection()),
00144                  Qt::QueuedConnection);
00145         doUdpPinging();
00146     } else {
00147         // After some time we discontinue sending out UDP pings and try
00148         // querying a UHC.
00149         // \todo Wait longer before issuing another UHC query! Hostname lookup
00150         // may take a few seconds.
00151         // \todo An empty chace could cause doUhcQuery to be called too often!
00152         // It is called from keepConnection() and doUdpPinging().
00153         keepConnection();
00154         doUhcQuery();
00155     }
00156     // \todo Add GWebCache support? Possible after completing the HTTP classes.
00157 }
00158 
00159 void ConnectionKeeper::keepConnection()
00160 {
00161     int count = p->slotAllocator->freeHandshakingSlots();
00162     if (count <= 0) {
00163         /*  We need to listen to the signal only while bootstrapping from UHC.
00164             The lack of free handshaking slots indicates we have some node
00165             addresses now. If none works, we will query a UHC again.    */
00166         disconnect (p->nodeCache,   SIGNAL (nodeAdded()),
00167                     this,           SLOT (keepConnection()));
00168         return;
00169     }
00170 
00171     // Let the rest of the slots for incomming connections!
00172     if (p->localPeer->isUltrapeer() && p->slotAllocator->isWellConnected())
00173         return;
00174 
00175     if (!p->localPeer->isUltrapeer() && !p->slotAllocator->hasFreeUltrapeerSlots())
00176         return;
00177 
00178     NodeSet nodes;
00179     nodes += p->getConnectNodes (count - nodes.count());
00180     nodes += p->getBootstrapNodes (count - nodes.count());
00181 
00182     foreach (NodeAddress nodeAddress, nodes) {
00183         p->localPeer->connectToPeer (nodeAddress.hostAddress().toString(),
00184                                      nodeAddress.hostPort());
00185     }
00186     if (nodes.count() == 0) {
00187         /*  \todo Seems like the nodeCache got empty!
00188             - If we have some connections than we will get some new pongs.
00189             Just have to wait, connect the signal from NodeCache::nodeAdded()
00190             Run a timer, if no new nodes in a while, do bootstrapping?
00191             - Should we change the avilability of all nodes in the cache to
00192             unavailable, so that we can keep trying?
00193         */
00194         doUhcQuery();
00195         return;
00196     }
00197 }
00198 
00199 void ConnectionKeeper::doUdpPinging()
00200 {
00201     int count = UdpPingsPerTry;
00202     // \todo Make sure after disconnectFromNetwork() all node cache entries are set to UnknownAvailability for optimal results:
00203 
00204     NodeSet nodes;
00205     nodes = p->getBootstrapNodes (count);
00206 qDebug() << "bootstrap nodes:" << nodes.count();
00207     foreach (NodeAddress nodeAddress, nodes) {
00208         //p->localPeer->connectToPeer (nodeAddress.hostAddress().toString(), nodeAddress.hostPort());
00209         // We set NodeBusy, so that the nodeAddresses not get dropped after
00210         // disconnect and we do not try them twice.
00211         p->nodeCache->updateNode (nodeAddress, NodeCache::NodeBusy);
00212         p->udpHostCache->queryNode (nodeAddress);
00213     }
00214     // Empty NodeCache, query UHC:
00215     if (nodes.count() == 0) {
00216         doUhcQuery();
00217         return;
00218     }
00219 }
00220 
00221 void ConnectionKeeper::doUhcQuery()
00222 {
00229     connect (p->nodeCache,  SIGNAL (nodeAdded()),
00230              this,          SLOT (keepConnection()),
00231              Qt::QueuedConnection);
00232     p->udpHostCache->queryCache();
00233     return;
00234 }