Send correct informations to a http tracker.
[tairent.git] / src / main / torrentclient.cpp
blob79bb07959c76b7f6b1818ee286418e063c8f315d
1 /***************************************************************************
2 * *
3 * Copyright (C) 2006 David Brodsky *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License as *
7 * published by the Free Software Foundation and appearing *
8 * in the file LICENSE.GPL included in the packaging of this file. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * General Public License for more details. *
14 * *
15 ***************************************************************************/
17 #include <tairon/core/log.h>
18 #include <tairon/net/timer.h>
20 #include "torrentclient.h"
22 #include "core/bitfield.h"
23 #include "connection.h"
24 #include "storage.h"
25 #include "torrentmanager.h"
26 #include "trackerclient.h"
27 #include "trackermanager.h"
29 namespace Tairent
32 namespace Main
35 /* {{{ TorrentClient::TorrentClient(TorrentStruct *, const String &, Storage *) */
36 TorrentClient::TorrentClient(TorrentStruct *t, const String &ih, Storage *s) : downloaded(0), infoHash(ih), storage(s), torrent(t), uploaded(0)
38 if (!s)
39 storage = new Storage(getMetaInfo()["info"]);
41 storage->pieceDownloadedSignal.connect(Tairon::Core::methodFunctor(this, &TorrentClient::pieceDownloadedAndChecked));
43 trackerClient = TrackerManager::self()->getTracker(this);
44 trackerClient->getPeers();
46 timer = new Tairon::Net::Timer();
47 timer->timeoutSignal.connect(Tairon::Core::threadMethodDFunctor(Tairon::Core::Thread::current(), this, &TorrentClient::rechoke));
48 timer->start(10000);
50 /* }}} */
52 /* {{{ TorrentClient::~TorrentClient() */
53 TorrentClient::~TorrentClient()
55 timer->destroy();
57 delete trackerClient;
59 // storage deletion is done by torrent manager
61 /* }}} */
63 /* {{{ TorrentClient::connectionClosed(Connection *) */
64 void TorrentClient::connectionClosed(Connection *c)
66 storage->unmapPieces(c->getPieceWriters());
67 c->destroyPieceWriters();
68 storage->unmapPieces(c->getPieceReader());
69 c->destroyPieceReaders();
71 storage->removeBitField(c->getBitField());
73 connections.erase(c);
74 delete c;
76 /* }}} */
78 /* {{{ TorrentClient::getInfoHash() */
79 const String &TorrentClient::getInfoHash()
81 return infoHash;
83 /* }}} */
85 /* {{{ TorrentClient::getMetaInfo() */
86 const Tairent::Core::BEncode &TorrentClient::getMetaInfo()
88 return *torrent->metaInfo;
90 /* }}} */
92 /* {{{ TorrentClient::getRemainingSize() */
93 uint64_t TorrentClient::getRemainingSize()
95 return storage->getRemainingSize();
97 /* }}} */
99 /* {{{ TorrentClient::getStorage() */
100 Storage *TorrentClient::getStorage()
102 return storage;
104 /* }}} */
106 /* {{{ TorrentClient::gotBitField(Connection *) */
107 void TorrentClient::gotBitField(Connection *c)
109 storage->addBitField(c->getBitField());
110 if (storage->shouldBeInterested(c->getBitField()))
111 c->sendInterested();
113 /* }}} */
115 /* {{{ TorrentClient::gotChoke(Connection *) */
116 void TorrentClient::gotChoke(Connection *c)
118 storage->reRequest(c->getRequested());
119 c->clearRequested();
121 /* }}} */
123 /* {{{ TorrentClient::gotHave(uint32_t, Connection *) */
124 void TorrentClient::gotHave(uint32_t index, Connection *c)
126 if (storage->gotHave(index)) {
127 c->sendInterested();
128 if (!c->isChoked())
129 requestMore(c);
132 /* }}} */
134 /* {{{ TorrentClient::gotInterestedChange(Connection *) */
135 void TorrentClient::gotInterestedChange(Connection *c)
137 if (c->isPeerChoked())
138 return;
139 rechoke();
141 /* }}} */
143 /* {{{ TorrentClient::gotPiece(uint32_t, uint32_t, Connection *) */
144 void TorrentClient::gotPiece(uint32_t index, uint32_t start, Connection *c)
146 storage->gotPiece(index, start, c);
147 downloaded += c->getPieceLength();
149 /* }}} */
151 /* {{{ TorrentClient::gotRequest(uint32_t, uint32_t, uint32_t, Connection *) */
152 void TorrentClient::gotRequest(uint32_t index, uint32_t start, uint32_t length, Connection *c)
154 if (c->isPeerChoked()) // we don't send pieces to the choked peers
155 return;
157 if (length > 16384) // too big piece
158 return; // TODO: close connection
160 storage->gotRequest(index, start, length, c);
162 /* }}} */
164 /* {{{ TorrentClient::gotUnchoke(Connection *) */
165 void TorrentClient::gotUnchoke(Connection *c)
167 if (c->isInterested())
168 requestMore(c);
170 /* }}} */
172 /* {{{ TorrentClient::newConnection(Connection *) */
173 void TorrentClient::newConnection(Connection *c)
175 connections.insert(c);
176 c->closedSignal.connect(Tairon::Core::methodFunctor(this, &TorrentClient::connectionClosed));
178 /* }}} */
180 /* {{{ TorrentClient::pieceDownloaded(uint32_t, uint32_t, Connection *) */
181 void TorrentClient::pieceDownloaded(uint32_t index, uint32_t start, Connection *c)
183 storage->pieceDownloaded(index, start);
184 requestMore(c);
186 /* }}} */
188 /* {{{ TorrentClient::pieceDownloadedAndChecked(uint32_t) */
189 void TorrentClient::pieceDownloadedAndChecked(uint32_t index)
191 for (std::set<Connection *>::const_iterator it = connections.begin(); it != connections.end(); ++it)
192 (*it)->sendHave(index);
194 /* }}} */
196 /* {{{ TorrentClient::pieceSent(uint32_t, uint32_t, Connection *) */
197 void TorrentClient::pieceSent(uint32_t index, uint32_t length, Connection *c)
199 storage->pieceSent(index);
200 uploaded += length;
202 /* }}} */
204 /* {{{ TorrentClient::rechoke() */
205 void TorrentClient::rechoke()
207 DEBUG("rechoking");
209 // mapping connections sorted by their upload rate
210 std::multimap<double, Connection *> preferred;
211 for (std::set<Connection *>::iterator it = connections.begin(); it != connections.end(); ++it)
212 if ((*it)->isPeerInterested() && !(*it)->isSnubbed())
213 preferred.insert(std::pair<double, Connection *>((*it)->getIncomingRate(), *it));
215 // number of preferred upload slots
216 int prefCount = preferred.size();
217 if (7 < prefCount) // TODO: config option: maxUploads
218 prefCount = 7;
220 // pick preferred connections
221 std::set<Connection *> mask;
222 for (std::multimap<double, Connection *>::reverse_iterator it = preferred.rbegin(); it != preferred.rend(); ++it) {
223 mask.insert(it->second);
224 if (!--prefCount)
225 break;
228 // number of connections that are not preferred and we want them unchoked
229 int count = 1;
230 if (1 - prefCount > count) // TODO: config option: minUploads
231 count = 1 - prefCount;
233 // unchoke preferred connections, remaining just put in a list
234 Connection **toUnchoke = new Connection *[connections.size() - mask.size()];
235 int toUnchokeIndex = 0;
236 for (std::set<Connection *>::iterator it = connections.begin(); it != connections.end(); ++it)
237 if (mask.count(*it))
238 (*it)->sendUnchoke();
239 else
240 toUnchoke[toUnchokeIndex++] = *it;
242 // shuffle the list
243 for (int i = 0; i < toUnchokeIndex; ++i) {
244 int r = random() % toUnchokeIndex;
245 Connection *temp = toUnchoke[i];
246 toUnchoke[i] = toUnchoke[r];
247 toUnchoke[r] = temp;
250 // unchoke remaining connections and choke the rest
251 for (int i = 0; i < toUnchokeIndex; ++i)
252 if (count) {
253 toUnchoke[i]->sendUnchoke();
254 if (toUnchoke[i]->isPeerInterested())
255 --count;
256 } else
257 toUnchoke[i]->sendChoke();
259 /* }}} */
261 /* {{{ TorrentClient::requestMore(Connection *) */
262 void TorrentClient::requestMore(Connection *c)
264 uint32_t index;
265 uint32_t start;
266 uint32_t length;
268 if (storage->pickPiece(c->getBitField(), index, start, length))
269 c->sendRequest(index, start, length);
270 else
271 c->sendNotInterested();
273 /* }}} */
275 /* {{{ TorrentClient::stop() */
276 void TorrentClient::stop()
278 trackerClient->stop();
279 // TODO: stop connections
281 /* }}} */
283 }; // namespace Main
285 }; // namespace Tairent
287 // vim: ai sw=4 ts=4 noet fdm=marker