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/log.h>
18 #include <tairon/net/timer.h>
20 #include "torrentclient.h"
22 #include "core/bitfield.h"
23 #include "connection.h"
25 #include "torrentmanager.h"
26 #include "trackerclient.h"
27 #include "trackermanager.h"
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)
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
));
52 /* {{{ TorrentClient::~TorrentClient() */
53 TorrentClient::~TorrentClient()
59 // storage deletion is done by torrent manager
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());
78 /* {{{ TorrentClient::getInfoHash() */
79 const String
&TorrentClient::getInfoHash()
85 /* {{{ TorrentClient::getMetaInfo() */
86 const Tairent::Core::BEncode
&TorrentClient::getMetaInfo()
88 return *torrent
->metaInfo
;
92 /* {{{ TorrentClient::getRemainingSize() */
93 uint64_t TorrentClient::getRemainingSize()
95 return storage
->getRemainingSize();
99 /* {{{ TorrentClient::getStorage() */
100 Storage
*TorrentClient::getStorage()
106 /* {{{ TorrentClient::gotBitField(Connection *) */
107 void TorrentClient::gotBitField(Connection
*c
)
109 storage
->addBitField(c
->getBitField());
110 if (storage
->shouldBeInterested(c
->getBitField()))
115 /* {{{ TorrentClient::gotChoke(Connection *) */
116 void TorrentClient::gotChoke(Connection
*c
)
118 storage
->reRequest(c
->getRequested());
123 /* {{{ TorrentClient::gotHave(uint32_t, Connection *) */
124 void TorrentClient::gotHave(uint32_t index
, Connection
*c
)
126 if (storage
->gotHave(index
)) {
134 /* {{{ TorrentClient::gotInterestedChange(Connection *) */
135 void TorrentClient::gotInterestedChange(Connection
*c
)
137 if (c
->isPeerChoked())
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();
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
157 if (length
> 16384) // too big piece
158 return; // TODO: close connection
160 storage
->gotRequest(index
, start
, length
, c
);
164 /* {{{ TorrentClient::gotUnchoke(Connection *) */
165 void TorrentClient::gotUnchoke(Connection
*c
)
167 if (c
->isInterested())
172 /* {{{ TorrentClient::newConnection(Connection *) */
173 void TorrentClient::newConnection(Connection
*c
)
175 connections
.insert(c
);
176 c
->closedSignal
.connect(Tairon::Core::methodFunctor(this, &TorrentClient::connectionClosed
));
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
);
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
);
196 /* {{{ TorrentClient::pieceSent(uint32_t, uint32_t, Connection *) */
197 void TorrentClient::pieceSent(uint32_t index
, uint32_t length
, Connection
*c
)
199 storage
->pieceSent(index
);
204 /* {{{ TorrentClient::rechoke() */
205 void TorrentClient::rechoke()
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
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
);
228 // number of connections that are not preferred and we want them unchoked
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
)
238 (*it
)->sendUnchoke();
240 toUnchoke
[toUnchokeIndex
++] = *it
;
243 for (int i
= 0; i
< toUnchokeIndex
; ++i
) {
244 int r
= random() % toUnchokeIndex
;
245 Connection
*temp
= toUnchoke
[i
];
246 toUnchoke
[i
] = toUnchoke
[r
];
250 // unchoke remaining connections and choke the rest
251 for (int i
= 0; i
< toUnchokeIndex
; ++i
)
253 toUnchoke
[i
]->sendUnchoke();
254 if (toUnchoke
[i
]->isPeerInterested())
257 toUnchoke
[i
]->sendChoke();
261 /* {{{ TorrentClient::requestMore(Connection *) */
262 void TorrentClient::requestMore(Connection
*c
)
268 if (storage
->pickPiece(c
->getBitField(), index
, start
, length
))
269 c
->sendRequest(index
, start
, length
);
271 c
->sendNotInterested();
275 /* {{{ TorrentClient::stop() */
276 void TorrentClient::stop()
278 trackerClient
->stop();
279 // TODO: stop connections
285 }; // namespace Tairent
287 // vim: ai sw=4 ts=4 noet fdm=marker