1 /***************************************************************************
3 * Copyright (C) 2006 David Brodsky *
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. *
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. *
15 ***************************************************************************/
17 #include <tairon/core/config.h>
18 #include <tairon/core/log.h>
19 #include <tairon/net/timer.h>
21 #include "torrentclient.h"
23 #include "core/bitfield.h"
24 #include "connection.h"
26 #include "torrentmanager.h"
27 #include "trackerclient.h"
28 #include "trackermanager.h"
36 /* {{{ TorrentClient::TorrentClient(TorrentStruct *, const String &, Storage *) */
37 TorrentClient::TorrentClient(TorrentStruct
*t
, const String
&ih
, Storage
*s
) : downloaded(0), infoHash(ih
), storage(s
), torrent(t
), uploaded(0)
40 storage
= new Storage(getMetaInfo()["info"]);
42 storage
->invalidDataSignal
.connect(Tairon::Core::methodFunctor(this, &TorrentClient::invalidDataReceived
));
43 storage
->pieceDownloadedSignal
.connect(Tairon::Core::methodFunctor(this, &TorrentClient::pieceDownloadedAndChecked
));
45 trackerClient
= TrackerManager::self()->getTracker(this);
46 trackerClient
->getPeers();
48 maxConnections
= atoi((*Tairon::Core::Config::self())["max-connections"]);
49 maxUploads
= atoi((*Tairon::Core::Config::self())["max-uploads"]);
50 minUploads
= atoi((*Tairon::Core::Config::self())["min-uploads"]);
52 timer
= new Tairon::Net::Timer();
53 timer
->timeoutSignal
.connect(Tairon::Core::threadMethodDFunctor(Tairon::Core::Thread::current(), this, &TorrentClient::rechoke
));
58 /* {{{ TorrentClient::~TorrentClient() */
59 TorrentClient::~TorrentClient()
65 // storage deletion is done by torrent manager
69 /* {{{ TorrentClient::addPeers(const std::list<PeerStruct> &) */
70 void TorrentClient::addPeers(const std::list
<PeerStruct
> &peers
)
72 String
clientID(TorrentManager::self()->getClientID());
74 for (std::list
<PeerStruct
>::const_iterator it
= peers
.begin(); it
!= peers
.end(); ++it
) {
75 if (connections
.count(it
->id
)) // we're already connected to this peer
77 if (pendingConnections
.count(it
->id
)) // we're trying to connect to this peer
79 if (it
->id
== clientID
) // don't connect to ourselves
82 Connection
*c
= new Connection(it
->ip
, it
->port
, it
->id
, this);
83 c
->closedSignal
.connect(Tairon::Core::methodFunctor(this, &TorrentClient::connectingFailed
));
84 pendingConnections
[it
->id
] = c
;
86 if (pendingConnections
.size() + connections
.size() >= maxConnections
)
92 /* {{{ TorrentClient::connectingFailed(Connection *) */
93 void TorrentClient::connectingFailed(Connection
*c
)
95 pendingConnections
.erase(c
->getPeerID());
100 /* {{{ TorrentClient::connectionClosed(Connection *) */
101 void TorrentClient::connectionClosed(Connection
*c
)
103 storage
->unmapPieces(c
->getPieceWriter());
104 c
->destroyPieceWriters();
105 storage
->unmapPieces(c
->getPieceReader());
106 c
->destroyPieceReaders();
108 storage
->removeBitField(c
->getBitField());
110 connections
.erase(c
->getPeerID());
115 /* {{{ TorrentClient::getInfoHash() */
116 const String
&TorrentClient::getInfoHash()
122 /* {{{ TorrentClient::getMetaInfo() */
123 const Tairent::Core::BEncode
&TorrentClient::getMetaInfo()
125 return *torrent
->metaInfo
;
129 /* {{{ TorrentClient::getRemainingSize() */
130 uint64_t TorrentClient::getRemainingSize()
132 return storage
->getRemainingSize();
136 /* {{{ TorrentClient::getStorage() */
137 Storage
*TorrentClient::getStorage()
143 /* {{{ TorrentClient::gotBitField(Connection *) */
144 void TorrentClient::gotBitField(Connection
*c
)
146 storage
->addBitField(c
->getBitField());
147 if (storage
->shouldBeInterested(c
->getBitField()))
152 /* {{{ TorrentClient::gotChoke(Connection *) */
153 void TorrentClient::gotChoke(Connection
*c
)
155 storage
->reRequest(c
->getRequested());
160 /* {{{ TorrentClient::gotHave(uint32_t, Connection *) */
161 void TorrentClient::gotHave(uint32_t index
, Connection
*c
)
163 if (storage
->gotHave(index
)) {
165 if (!c
->isChoked() && (c
->getRequestsCount() < 5)) // TODO: do it better
171 /* {{{ TorrentClient::gotInterestedChange(Connection *) */
172 void TorrentClient::gotInterestedChange(Connection
*c
)
174 if (c
->isPeerChoked())
180 /* {{{ TorrentClient::gotPiece(uint32_t, uint32_t, Connection *) */
181 void TorrentClient::gotPiece(uint32_t index
, uint32_t start
, Connection
*c
)
183 downloaded
+= c
->getPieceLength();
184 storage
->gotPiece(index
, start
, c
);
188 /* {{{ TorrentClient::gotUnchoke(Connection *) */
189 void TorrentClient::gotUnchoke(Connection
*c
)
191 if (c
->isInterested())
196 /* {{{ TorrentClient::invalidDataReceived(Connection *) */
197 void TorrentClient::invalidDataReceived(Connection
*c
)
204 /* {{{ TorrentClient::newConnection(Connection *) */
205 void TorrentClient::newConnection(Connection
*c
)
207 if (pendingConnections
.count(c
->getPeerID()))
208 pendingConnections
.erase(c
->getPeerID());
210 if (pendingConnections
.size() + connections
.size() >= maxConnections
) {
216 if (connections
.count(c
->getPeerID())) { // already connected peer
222 connections
[c
->getPeerID()] = c
;
223 c
->closedSignal
.connect(Tairon::Core::methodFunctor(this, &TorrentClient::connectionClosed
));
227 /* {{{ TorrentClient::pieceDownloaded(uint32_t, uint32_t, Connection *) */
228 void TorrentClient::pieceDownloaded(uint32_t index
, uint32_t start
, Connection
*c
)
230 storage
->pieceDownloaded(index
, start
);
235 /* {{{ TorrentClient::pieceDownloadedAndChecked(uint32_t) */
236 void TorrentClient::pieceDownloadedAndChecked(uint32_t index
)
238 std::map
<String
, Connection
*> temp(connections
);
239 for (std::map
<String
, Connection
*>::const_iterator it
= temp
.begin(); it
!= temp
.end(); ++it
)
240 it
->second
->sendHave(index
);
244 /* {{{ TorrentClient::pieceSent(uint32_t, uint32_t, Connection *) */
245 void TorrentClient::pieceSent(uint32_t index
, uint32_t length
, Connection
*c
)
247 storage
->pieceSent(index
);
252 /* {{{ TorrentClient::rechoke() */
253 void TorrentClient::rechoke()
257 // mapping connections sorted by their upload rate
258 std::multimap
<double, Connection
*> preferred
;
259 for (std::map
<String
, Connection
*>::iterator it
= connections
.begin(); it
!= connections
.end(); ++it
)
260 if (it
->second
->isPeerInterested() && !it
->second
->isSnubbed())
261 preferred
.insert(std::pair
<double, Connection
*>(it
->second
->getIncomingRate(), it
->second
));
263 // number of preferred upload slots
264 int prefCount
= preferred
.size();
265 if (maxUploads
< prefCount
)
268 // pick preferred connections
269 std::set
<Connection
*> mask
;
270 for (std::multimap
<double, Connection
*>::reverse_iterator it
= preferred
.rbegin(); it
!= preferred
.rend(); ++it
) {
271 mask
.insert(it
->second
);
276 // number of connections that are not preferred and we want them unchoked
278 if (1 - prefCount
> count
)
279 count
= 1 - prefCount
;
281 // unchoke preferred connections, remaining just put in a list
282 Connection
**toUnchoke
= new Connection
*[connections
.size() - mask
.size()];
283 int toUnchokeIndex
= 0;
284 std::map
<String
, Connection
*> temp(connections
);
285 for (std::map
<String
, Connection
*>::iterator it
= temp
.begin(); it
!= temp
.end(); ++it
)
286 if (mask
.count(it
->second
))
287 it
->second
->sendUnchoke();
289 toUnchoke
[toUnchokeIndex
++] = it
->second
;
292 for (int i
= 0; i
< toUnchokeIndex
; ++i
) {
293 int r
= random() % toUnchokeIndex
;
294 Connection
*temp
= toUnchoke
[i
];
295 toUnchoke
[i
] = toUnchoke
[r
];
299 // unchoke remaining connections and choke the rest
300 for (int i
= 0; i
< toUnchokeIndex
; ++i
)
302 if (toUnchoke
[i
]->isPeerInterested())
304 toUnchoke
[i
]->sendUnchoke();
306 toUnchoke
[i
]->sendChoke();
312 /* {{{ TorrentClient::requestMore(Connection *) */
313 void TorrentClient::requestMore(Connection
*c
)
319 if (storage
->pickPiece(c
->getBitField(), index
, start
, length
))
320 c
->sendRequest(index
, start
, length
);
322 c
->sendNotInterested();
326 /* {{{ TorrentClient::stop() */
327 void TorrentClient::stop()
329 trackerClient
->stop();
331 for (std::map
<String
, Connection
*>::const_iterator it
= connections
.begin(); it
!= connections
.end(); ++it
) {
337 for (std::map
<String
, Connection
*>::const_iterator it
= pendingConnections
.begin(); it
!= pendingConnections
.end(); ++it
) {
341 pendingConnections
.clear();
347 }; // namespace Tairent
349 // vim: ai sw=4 ts=4 noet fdm=marker