UdpHostCache.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "Qt.h"
00024 #include "UdpHostCache.h"
00025 #include "PingHandler.h"
00026 #include "Imports.cpp"
00027
00028 using namespace Gnutella::Bootstrapping;
00029
00030 namespace Gnutella {
00031 namespace Bootstrapping {
00032
00033 enum Constants
00034 {
00035 SessionReadTimeout = 10000
00036 };
00037
00038 struct CacheInfo
00039 {
00040 NodeAddress address;
00041 int tryCount;
00042 int successCount;
00043 int failureCount;
00044 bool isQuerying;
00045 bool gotPong;
00046
00047
00048
00049 CacheInfo (const NodeAddress &address_ = NodeAddress())
00050 : address (address_), tryCount (0), successCount (0), failureCount (0),
00051 isQuerying (false), gotPong (false)
00052 {}
00053 };
00054
00055 class UdpHostCachePrivate
00056 {
00057 REFERENCE_OBJECT (UdpHostCachePrivate)
00058
00059 public:
00060 typedef QMap <UdpConnection *, CacheInfo *> ConnectingConnections;
00061 typedef QMap <PacketSession *, CacheInfo *> Sessions;
00062 typedef QQueue <CacheInfo *> Caches;
00063
00064 UdpSwitch *udpSwitch;
00065 PingHandler *pingHandler;
00066 ConnectingConnections connectingConnections;
00067 Sessions sessions;
00068 Caches caches;
00069
00070 UdpHostCachePrivate() : udpSwitch (0), pingHandler (0),
00071 connectingConnections(), sessions(), caches()
00072 {}
00073 };
00074
00075 }
00076 }
00077
00078 UdpHostCache::UdpHostCache (UdpSwitch *udpSwitch, PingHandler *pingHandler)
00079 : p (new UdpHostCachePrivate)
00080 {
00081 p->udpSwitch = udpSwitch;
00082 p->pingHandler = pingHandler;
00083
00084
00085
00086 addCache (NodeAddress ("secondary.udp-host-cache.com", 9999));
00087 }
00088
00089 UdpHostCache::~UdpHostCache()
00090 {
00091
00092 while (!p->caches.isEmpty())
00093 delete p->caches.dequeue();
00094 delete p;
00095 }
00096
00097 void UdpHostCache::addCache (const NodeAddress &cacheAddress)
00098 {
00099 foreach (CacheInfo *info, p->caches) {
00100 if (info->address == cacheAddress) {
00101
00102 return;
00103 }
00104 }
00105 CacheInfo *info = new CacheInfo (cacheAddress);
00106 p->caches.enqueue (info);
00107 }
00108
00109 void UdpHostCache::removeCache (const NodeAddress &cacheAddress)
00110 {
00111
00112 foreach (CacheInfo *info, p->caches) {
00113 if (info->address == cacheAddress) {
00114 p->caches.removeAll (info);
00115 delete info;
00116 break;
00117 }
00118 }
00119 }
00120
00122
00126 void UdpHostCache::queryNode (const NodeAddress &address)
00127 {
00128 doQuery (address, 0);
00129 }
00130
00131 void UdpHostCache::queryCache()
00132 {
00133
00134
00135
00136
00137 if (p->caches.front()->isQuerying)
00138 return;
00139
00140 CacheInfo *info = p->caches.dequeue();
00141 info->tryCount++;
00142 info->isQuerying = true;
00143 info->gotPong = false;
00144 p->caches.enqueue (info);
00145
00146 doQuery (info->address, info);
00147 }
00148
00149 void UdpHostCache::doQuery (const NodeAddress &address, CacheInfo *info)
00150 {
00151 UdpConnection *connection = new UdpConnection (p->udpSwitch);
00152
00153 connect (connection, SIGNAL (connectionEstablished()),
00154 this, SLOT (connectionEstablishedSlot()),
00155 Qt::QueuedConnection);
00156 connect (connection, SIGNAL (connectionFailed()),
00157 this, SLOT (connectionFailedSlot()),
00158 Qt::QueuedConnection);
00159
00160 p->connectingConnections.insert (connection, info);
00161 connection->connectToNode (address);
00162 }
00163
00164 PacketSession * UdpHostCache::createSession (UdpConnection *connection)
00165 {
00166 PacketSession *session = new PacketSession (connection, NodeInfo());
00167 session->setReadTimeout (SessionReadTimeout);
00168
00169 connect (session, SIGNAL (packetRead (Packet &, PacketSession *)),
00170 this, SLOT (processPacket (Packet &, PacketSession *)));
00171 connect (session, SIGNAL (sessionClosed (PacketSession *)),
00172 this, SLOT (closeSession (PacketSession *)),
00173 Qt::QueuedConnection);
00174
00175 return session;
00176 }
00177
00178 void UdpHostCache::connectionAcceptedSlot (UdpConnection *incomingConnection)
00179 {
00180 PacketSession *session = createSession (incomingConnection);
00181 p->sessions.insert (session, 0);
00182 }
00183
00184 void UdpHostCache::connectionFailedSlot()
00185 {
00186 UdpConnection *connection = reinterpret_cast <UdpConnection *> (sender());
00187 Q_ASSERT (connection != 0);
00188 Q_ASSERT (p->connectingConnections.contains (connection));
00189
00190 CacheInfo *info = p->connectingConnections.value (connection);
00191 info->failureCount++;
00192 info->isQuerying = false;
00193 p->connectingConnections.remove (connection);
00194
00195 connection->disconnect (this);
00196 delete connection;
00197 }
00198
00199 void UdpHostCache::connectionEstablishedSlot()
00200 {
00201 UdpConnection *connection = reinterpret_cast <UdpConnection *> (sender());
00202 Q_ASSERT (connection != 0);
00203 Q_ASSERT (p->connectingConnections.contains (connection));
00204
00205 CacheInfo *info = p->connectingConnections.value (connection);
00206 p->connectingConnections.remove (connection);
00207
00208 PacketSession *session = createSession (connection);
00209 p->sessions.insert (session, info);
00210
00211 SupportsCachedPongs scp (SupportsCachedPongs::UltrapeerPreference);
00212
00213 Ping ping;
00214 GgepBlock ggepBlock;
00215 ggepBlock.addExtension (scp);
00216 ping.setGgepBlock (ggepBlock);
00217
00218 ping.setTtl (1);
00219 ping.setHops (0);
00220
00221 session->sendPacket (ping);
00222 }
00223
00224 void UdpHostCache::processPacket (Packet &packet, PacketSession *session)
00225 {
00226 Q_ASSERT (p->sessions.contains (session));
00227 using namespace Gnutella::Packets;
00228
00229 switch (packet.payloadDescriptor())
00230 {
00231 case PingDescriptor:
00232 {
00233 qDebug() << "received UDP ping, sending a pong back";
00234 Ping &ping = Ping::castFrom (packet);
00235 Pong pong = p->pingHandler->makePongFor (ping);
00236 session->sendPacket (pong);
00237 }
00238 break;
00239 case PongDescriptor:
00240 {
00241 CacheInfo *info = p->sessions.value (session);
00242 if (info) {
00243 info->gotPong = true;
00244 info->successCount++;
00245 }
00246 Pong &pong = Pong::castFrom (packet);
00247 qDebug() << "received UDP pong: " << pong.ipAddress().toString() << pong.port();
00248 emit receivedPong (pong);
00249
00250
00251 }
00252 break;
00253 case QueryDescriptor:
00254 case QueryHitsDescriptor:
00255 case PushDescriptor:
00256 case ByeDescriptor:
00257 case IbmcDescriptor:
00258 case QueryRoutingDescriptor:
00259 case OpenVendorDescriptor:
00260 case StandardVendorDescriptor:
00261 default:
00262 break;
00263 }
00264 }
00265
00266 void UdpHostCache::closeSession (PacketSession *session)
00267 {
00268 Q_ASSERT (p->sessions.contains (session));
00269
00270 CacheInfo *info = p->sessions.value (session);
00271 if (info) {
00272 if (!info->gotPong)
00273 info->failureCount++;
00274 info->isQuerying = false;
00275 info->gotPong = false;
00276 }
00277 p->sessions.remove (session);
00278 session->disconnect (this);
00279 delete session;
00280 }
00281
00282 bool UdpHostCache::loadCaches (const QString &filename)
00283 {
00284 QFile file (filename);
00285 if (!file.exists() || !file.open (QIODevice::ReadOnly))
00286 return false;
00287
00288 QDataStream stream (&file);
00289
00290 int count = 0;
00291 stream >> count;
00292
00293
00294 while (count-- > 0) {
00295 CacheInfo *info = new CacheInfo;
00296
00297 stream >> info->address;
00298 stream >> info->tryCount;
00299 stream >> info->successCount;
00300 stream >> info->failureCount;
00301
00302 info->isQuerying = false;
00303 info->gotPong = false;
00304
00305 p->caches.append (info);
00306 }
00307 return true;
00308 }
00309
00310 bool UdpHostCache::storeCaches (const QString &filename)
00311 {
00312
00313
00314 QFile file (filename);
00315
00316 if (!file.open (QIODevice::WriteOnly))
00317 return false;
00318
00319 QDataStream stream (&file);
00320
00321
00322 stream << p->caches.count();
00323 UdpHostCachePrivate::Caches::iterator i = p->caches.begin();
00324 UdpHostCachePrivate::Caches::iterator e = p->caches.end();
00325 for (; i != e; i++) {
00326 CacheInfo *info = *i;
00327 stream << info->address;
00328 stream << info->tryCount;
00329 stream << info->successCount;
00330 stream << info->failureCount;
00331 }
00332 return true;
00333 }