use of org.klomp.snark.dht.DHT in project i2p.i2p by i2p.
the class I2PSnarkServlet method writeTorrents.
/**
* @param canWrite is the data directory writable?
* @return true if on first page
*/
private boolean writeTorrents(PrintWriter out, HttpServletRequest req, boolean canWrite) throws IOException {
/**
* dl, ul, down rate, up rate, peers, size
*/
final long[] stats = { 0, 0, 0, 0, 0, 0 };
String peerParam = req.getParameter("p");
String stParam = req.getParameter("st");
List<Snark> snarks = getSortedSnarks(req);
boolean isForm = _manager.util().connected() || !snarks.isEmpty();
if (isForm) {
out.write("<form action=\"_post\" method=\"POST\">\n");
writeHiddenInputs(out, req, null);
}
out.write(TABLE_HEADER);
// Opera and text-mode browsers: no   and no input type=image values submitted
// Using a unique name fixes Opera, except for the buttons with js confirms, see below
String ua = req.getHeader("User-Agent");
boolean isDegraded = ua != null && ServletUtil.isTextBrowser(ua);
boolean noThinsp = isDegraded || (ua != null && ua.startsWith("Opera"));
// pages
int start = 0;
int total = snarks.size();
if (stParam != null) {
try {
start = Math.max(0, Math.min(total - 1, Integer.parseInt(stParam)));
} catch (NumberFormatException nfe) {
}
}
int pageSize = Math.max(_manager.getPageSize(), 5);
String currentSort = req.getParameter("sort");
boolean showSort = total > 1;
out.write("<tr><th class=\"snarkGraphicStatus\">");
String sort = ("2".equals(currentSort)) ? "-2" : "2";
if (showSort) {
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
String tx = _t("Status");
out.write(toThemeImg("status", tx, showSort ? _t("Sort by {0}", tx) : tx));
if (showSort)
out.write("</a>");
out.write("</th>\n<th class=\"snarkTorrentStatus\">");
if (_manager.util().connected() && !snarks.isEmpty()) {
out.write(" <a href=\"" + _contextPath + '/');
if (peerParam != null) {
// disable peer view
out.write(getQueryString(req, "", null, null));
out.write("\">");
tx = _t("Hide Peers");
out.write(toThemeImg("hidepeers", tx, tx));
} else {
// enable peer view
out.write(getQueryString(req, "1", null, null));
out.write("\">");
tx = _t("Show Peers");
out.write(toThemeImg("showpeers", tx, tx));
}
out.write("</a>\n");
}
out.write("</th>\n<th colspan=\"2\" align=\"left\">");
// cycle through sort by name or type
boolean isTypeSort = false;
if (showSort) {
if (currentSort == null || "0".equals(currentSort) || "1".equals(currentSort)) {
sort = "-1";
} else if ("-1".equals(currentSort)) {
sort = "12";
isTypeSort = true;
} else if ("12".equals(currentSort)) {
sort = "-12";
isTypeSort = true;
} else {
sort = "";
}
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
tx = _t("Torrent");
out.write(toThemeImg("torrent", tx, showSort ? _t("Sort by {0}", (isTypeSort ? _t("File type") : tx)) : tx));
if (showSort)
out.write("</a>");
out.write("</th>\n<th id=\"pagenav\" align=\"center\">");
if (total > 0 && (start > 0 || total > pageSize)) {
writePageNav(out, req, start, pageSize, total, noThinsp);
}
out.write("</th>\n<th class=\"snarkTorrentETA\" align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) {
if (showSort) {
sort = ("-4".equals(currentSort)) ? "4" : "-4";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
// Translators: Please keep short or translate as " "
tx = _t("ETA");
out.write(toThemeImg("eta", tx, showSort ? _t("Sort by {0}", _t("Estimated time remaining")) : _t("Estimated time remaining")));
if (showSort)
out.write("</a>");
}
out.write("</th>\n<th class=\"snarkTorrentDownloaded\" align=\"right\">");
// cycle through sort by size or downloaded
boolean isDlSort = false;
if (showSort) {
if ("-5".equals(currentSort)) {
sort = "5";
} else if ("5".equals(currentSort)) {
sort = "-6";
isDlSort = true;
} else if ("-6".equals(currentSort)) {
sort = "6";
isDlSort = true;
} else {
sort = "-5";
}
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
// Translators: Please keep short or translate as " "
tx = _t("RX");
out.write(toThemeImg("head_rx", tx, showSort ? _t("Sort by {0}", (isDlSort ? _t("Downloaded") : _t("Size"))) : _t("Downloaded")));
if (showSort)
out.write("</a>");
out.write("</th>\n<th class=\"snarkTorrentUploaded\" align=\"right\">");
boolean isRatSort = false;
if (!snarks.isEmpty()) {
// cycle through sort by uploaded or ratio
boolean nextRatSort = false;
if (showSort) {
if ("-7".equals(currentSort)) {
sort = "7";
} else if ("7".equals(currentSort)) {
sort = "-11";
nextRatSort = true;
} else if ("-11".equals(currentSort)) {
sort = "11";
nextRatSort = true;
isRatSort = true;
} else if ("11".equals(currentSort)) {
sort = "-7";
isRatSort = true;
} else {
sort = "-7";
}
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
// Translators: Please keep short or translate as " "
tx = _t("TX");
out.write(toThemeImg("head_tx", tx, showSort ? _t("Sort by {0}", (nextRatSort ? _t("Upload ratio") : _t("Uploaded"))) : _t("Uploaded")));
if (showSort)
out.write("</a>");
}
out.write("</th>\n<th class=\"snarkTorrentRateDown\" align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) {
if (showSort) {
sort = ("-8".equals(currentSort)) ? "8" : "-8";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
// Translators: Please keep short or translate as " "
tx = _t("RX Rate");
out.write(toThemeImg("head_rxspeed", tx, showSort ? _t("Sort by {0}", _t("Down Rate")) : _t("Down Rate")));
if (showSort)
out.write("</a>");
}
out.write("</th>\n<th class=\"snarkTorrentRateUp\" align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) {
if (showSort) {
sort = ("-9".equals(currentSort)) ? "9" : "-9";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\">");
}
// Translators: Please keep short or translate as " "
tx = _t("TX Rate");
out.write(toThemeImg("head_txspeed", tx, showSort ? _t("Sort by {0}", _t("Up Rate")) : _t("Up Rate")));
if (showSort)
out.write("</a>");
}
out.write("</th>\n<th class=\"snarkTorrentAction\" align=\"center\">");
if (_manager.isStopping()) {
out.write(" ");
} else if (_manager.util().connected()) {
if (isDegraded)
out.write("<a href=\"" + _contextPath + "/?action=StopAll&nonce=" + _nonce + "\"><img title=\"");
else {
// http://www.onenaught.com/posts/382/firefox-4-change-input-type-image-only-submits-x-and-y-not-name
// out.write("<input type=\"image\" name=\"action\" value=\"StopAll\" title=\"");
out.write("<input type=\"image\" name=\"action_StopAll\" value=\"foo\" title=\"");
}
out.write(_t("Stop all torrents and the I2P tunnel"));
out.write("\" src=\"" + _imgPath + "stop_all.png\" alt=\"");
out.write(_t("Stop All"));
out.write("\">");
if (isDegraded)
out.write("</a>");
for (Snark s : snarks) {
if (s.isStopped()) {
// show startall too
if (isDegraded)
out.write("<a href=\"" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_StartAll\" value=\"foo\" title=\"");
out.write(_t("Start all stopped torrents"));
out.write("\" src=\"" + _imgPath + "start_all.png\" alt=\"");
out.write(_t("Start All"));
out.write("\">");
if (isDegraded)
out.write("</a>");
break;
}
}
} else if ((!_manager.util().isConnecting()) && !snarks.isEmpty()) {
if (isDegraded)
out.write("<a href=\"" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
else
out.write("<input type=\"image\" name=\"action_StartAll\" value=\"foo\" title=\"");
out.write(_t("Start all torrents and the I2P tunnel"));
out.write("\" src=\"" + _imgPath + "start_all.png\" alt=\"");
out.write(_t("Start All"));
out.write("\">");
if (isDegraded)
out.write("</a>");
} else {
out.write(" ");
}
out.write("</th></tr>\n");
out.write("</thead>\n");
String uri = _contextPath + '/';
boolean showDebug = "2".equals(peerParam);
for (int i = 0; i < total; i++) {
Snark snark = snarks.get(i);
boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam);
boolean hide = i < start || i >= start + pageSize;
displaySnark(out, req, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug, hide, isRatSort, canWrite);
}
if (total == 0) {
out.write("<tr class=\"snarkTorrentNoneLoaded\">" + "<td colspan=\"11\">");
synchronized (this) {
File dd = _resourceBase;
if (!dd.exists() && !dd.mkdirs()) {
out.write(_t("Data directory cannot be created") + ": " + DataHelper.escapeHTML(dd.toString()));
} else if (!dd.isDirectory()) {
out.write(_t("Not a directory") + ": " + DataHelper.escapeHTML(dd.toString()));
} else if (!dd.canRead()) {
out.write(_t("Unreadable") + ": " + DataHelper.escapeHTML(dd.toString()));
} else if (canWrite) {
out.write(_t("No write permissions for data directory") + ": " + DataHelper.escapeHTML(dd.toString()));
} else {
out.write(_t("No torrents loaded."));
}
}
out.write("</td></tr>\n");
} else /**
* if (snarks.size() > 1)
*/
{
out.write("<tfoot><tr>\n" + " <th id=\"snarkTorrentTotals\" align=\"left\" colspan=\"6\">");
out.write("<span id=\"totals\">");
out.write(_t("Totals"));
out.write(": ");
out.write(ngettext("1 torrent", "{0} torrents", total));
out.write(", ");
out.write(formatSize(stats[5]));
if (_manager.util().connected() && total > 0) {
out.write(", ");
out.write(ngettext("1 connected peer", "{0} connected peers", (int) stats[4]));
}
DHT dht = _manager.util().getDHT();
if (dht != null) {
int dhts = dht.size();
if (dhts > 0) {
out.write(", <span>");
out.write(ngettext("1 DHT peer", "{0} DHT peers", dhts));
out.write("</span>");
}
}
String IPString = _manager.util().getOurIPString();
if (!IPString.equals("unknown")) {
// Only truncate if it's an actual dest
out.write("; <span id=\"ourDest\">");
out.write(_t("Dest"));
out.write(": <tt title=\"");
out.write(_t("Our destination (identity) for this session"));
out.write("\">");
out.write(IPString.substring(0, 4));
out.write("</tt></span>");
}
out.write("</span>");
out.write("</th>\n");
if (_manager.util().connected() && total > 0) {
out.write(" <th class=\"snarkTorrentDownloaded\" align=\"right\">" + formatSize(stats[0]) + "</th>\n" + " <th class=\"snarkTorrentUploaded\" align=\"right\">" + formatSize(stats[1]) + "</th>\n" + " <th class=\"snarkTorrentRateDown\" align=\"right\">" + formatSizeDec(stats[2]) + "ps</th>\n" + " <th class=\"snarkTorrentRateUp\" align=\"right\">" + formatSizeDec(stats[3]) + "ps</th>\n" + " <th class=\"snarkTorrentAction\"></th>");
} else {
out.write("<th colspan=\"5\"></th>");
}
// TODO javascript handler to remember checkbox status for debug panel visibility (otherwise resets with ajax/meta refresh)
if (dht != null) {
if (showDebug) {
out.write("</tr>\n<tr class=\"dhtDebug\">");
out.write("<th colspan=\"11\">");
out.write("<div id=\"dhtDebugPanel\">");
out.write("<input class=\"toggle_input\" id=\"toggle_debug\" type=\"checkbox\"><label class=\"toggleview\" for=\"toggle_debug\">");
out.write(toThemeImg("debug"));
out.write(' ');
out.write(_t("Dht Debug"));
out.write("</label><div id=\"dhtDebugInner\">");
out.write(dht.renderStatusHTML());
out.write("</div></div></th>");
}
}
out.write("</tr></tfoot>\n");
}
out.write("</table>");
if (isForm)
out.write("</form>\n");
return start == 0;
}
use of org.klomp.snark.dht.DHT in project i2p.i2p by i2p.
the class TrackerClient method getPeersFromTrackers.
/**
* @return max peers seen
*/
private int getPeersFromTrackers(List<TCTracker> trckrs) {
// -1 in magnet mode
long left = coordinator.getLeft();
// First time we got a complete download?
boolean newlyCompleted;
if (!completed && left == 0) {
completed = true;
newlyCompleted = true;
} else {
newlyCompleted = false;
}
// *** loop once for each tracker
int maxSeenPeers = 0;
for (TCTracker tr : trckrs) {
if ((!stop) && (!tr.stop) && (completed || coordinator.needOutboundPeers() || !tr.started) && (newlyCompleted || System.currentTimeMillis() > tr.lastRequestTime + tr.interval)) {
try {
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
long len = snark.getTotalLength();
if (len > 0 && downloaded > len)
downloaded = len;
left = coordinator.getLeft();
String event;
if (!tr.started) {
event = STARTED_EVENT;
} else if (newlyCompleted) {
event = COMPLETED_EVENT;
} else {
event = NO_EVENT;
}
TrackerInfo info = doRequest(tr, infoHash, peerID, uploaded, downloaded, left, event);
snark.setTrackerProblems(null);
tr.trackerProblems = null;
tr.registerFails = 0;
tr.consecutiveFails = 0;
if (tr.isPrimary)
consecutiveFails = 0;
runStarted = true;
tr.started = true;
tr.seenPeers = info.getPeerCount();
if (// update rising number quickly
snark.getTrackerSeenPeers() < tr.seenPeers)
snark.setTrackerSeenPeers(tr.seenPeers);
// just for update torrent
if (completed && tr.isPrimary && snark.isAutoStoppable() && !snark.isChecking() && info.getSeedCount() > 100 && coordinator.getPeerCount() <= 0 && _util.getContext().clock().now() > _startedOn + 30 * 60 * 1000 && snark.getTotalLength() > 0 && uploaded >= snark.getTotalLength() / 2) {
if (_log.shouldLog(Log.WARN))
_log.warn("Auto stopping " + snark.getBaseName());
snark.setAutoStoppable(false);
snark.stopTorrent();
return tr.seenPeers;
}
Set<Peer> peers = info.getPeers();
// pass everybody over to our tracker
DHT dht = _util.getDHT();
if (dht != null) {
for (Peer peer : peers) {
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash(), // TODO actual seed/leech status
false);
}
}
if (coordinator.needOutboundPeers()) {
// we only want to talk to new people if we need things
// from them (duh)
List<Peer> ordered = new ArrayList<Peer>(peers);
Random r = _util.getContext().random();
Collections.shuffle(ordered, r);
Iterator<Peer> it = ordered.iterator();
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
Peer cur = it.next();
// only delay if we actually make an attempt to add peer
if (coordinator.addPeer(cur) && it.hasNext()) {
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
}
}
}
}
} catch (IOException ioe) {
// Probably not fatal (if it doesn't last to long...)
if (_log.shouldLog(Log.WARN))
_log.warn("Could not contact tracker at '" + tr.announce + "': " + ioe);
tr.trackerProblems = ioe.getMessage();
// ... and only if we don't have any peers at all. Otherwise, PEX/DHT will save us.
if (tr.isPrimary && coordinator.getPeers() <= 0 && (!completed || _util.getDHT() == null || _util.getDHT().size() <= 0))
snark.setTrackerProblems(tr.trackerProblems);
String tplc = tr.trackerProblems.toLowerCase(Locale.US);
if (tplc.startsWith(NOT_REGISTERED) || tplc.startsWith(NOT_REGISTERED_2) || tplc.startsWith(NOT_REGISTERED_3) || tplc.startsWith(ERROR_GOT_HTML)) {
// } else { // hopefully each on the opentrackers list is really open
if (tr.registerFails++ > MAX_REGISTER_FAILS || // no use retrying if we aren't seeding
!completed || // fake msg from doRequest()
tplc.startsWith(ERROR_GOT_HTML) || (!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
if (_log.shouldLog(Log.WARN))
_log.warn("Not longer announcing to " + tr.announce + " : " + tr.trackerProblems + " after " + tr.registerFails + " failures");
tr.stop = true;
//
}
if (++tr.consecutiveFails == MAX_CONSEC_FAILS) {
tr.seenPeers = 0;
if (tr.interval < LONG_SLEEP)
// slow down
tr.interval = LONG_SLEEP;
}
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Not announcing to " + tr.announce + " last announce was " + new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval));
}
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
maxSeenPeers = tr.seenPeers;
}
return maxSeenPeers;
}
use of org.klomp.snark.dht.DHT in project i2p.i2p by i2p.
the class PeerCoordinator method sendDHT.
/**
* Send a DHT message to the peer, if we both support DHT.
* @since DHT
*/
void sendDHT(Peer peer) {
DHT dht = _util.getDHT();
if (dht == null)
return;
Map<String, BEValue> handshake = peer.getHandshakeMap();
if (handshake == null)
return;
BEValue bev = handshake.get("m");
if (bev == null)
return;
try {
if (bev.getMap().get(ExtensionHandler.TYPE_DHT) != null)
ExtensionHandler.sendDHT(peer, dht.getPort(), dht.getRPort());
} catch (InvalidBEncodingException ibee) {
}
}
use of org.klomp.snark.dht.DHT in project i2p.i2p by i2p.
the class PeerCheckerTask method run.
public void run() {
_runCount++;
List<Peer> peerList = coordinator.peerList();
if (peerList.isEmpty() || coordinator.halted()) {
coordinator.setRateHistory(0, 0);
return;
}
// Calculate total uploading and worst downloader.
long worstdownload = Long.MAX_VALUE;
Peer worstDownloader = null;
int uploaders = 0;
int interestedUploaders = 0;
int removedCount = 0;
long uploaded = 0;
long downloaded = 0;
// Keep track of peers we remove now,
// we will add them back to the end of the list.
List<Peer> removed = new ArrayList<Peer>();
int uploadLimit = coordinator.allowedUploaders();
boolean overBWLimit = coordinator.overUpBWLimit();
if (_log.shouldLog(Log.DEBUG))
_log.debug("START peers: " + peerList.size() + " uploaders: " + coordinator.getUploaders() + " interested: " + coordinator.getInterestedUploaders() + " limit: " + uploadLimit + " overBW? " + overBWLimit);
DHT dht = _util.getDHT();
boolean fetchComments = _util.utCommentsEnabled();
int i = 0;
for (Peer peer : peerList) {
i++;
// Remove dying peers
if (!peer.isConnected()) {
// coordinator.peerCount = coordinator.peers.size();
continue;
}
if (peer.getInactiveTime() > PeerCoordinator.MAX_INACTIVE) {
if (_log.shouldLog(Log.WARN))
_log.warn("Disconnecting peer idle " + DataHelper.formatDuration(peer.getInactiveTime()) + ": " + peer);
peer.disconnect();
continue;
}
// from some other torrent
if (peer.isInterested() && !peer.isChoking())
uploaders++;
long upload = peer.getUploaded();
uploaded += upload;
long download = peer.getDownloaded();
downloaded += download;
peer.setRateHistory(upload, download);
peer.resetCounters();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(peer + ":" + " ul: " + upload * 1024 / KILOPERSECOND + " dl: " + download * 1024 / KILOPERSECOND + " i: " + peer.isInterested() + " I: " + peer.isInteresting() + " c: " + peer.isChoking() + " C: " + peer.isChoked());
}
// Choke a percentage of them rather than all so it isn't so drastic...
// unless this torrent is over the limit all by itself.
// choke 5/8 of the time when seeding and 3/8 when leeching
boolean overBWLimitChoke = upload > 0 && ((overBWLimit && (random.nextInt(8) > (coordinator.completed() ? 2 : 4))) || (coordinator.overUpBWLimit(uploaded)));
// If we are at our max uploaders and we have lots of other
// interested peers try to make some room.
// (Note use of coordinator.uploaders)
int cup = coordinator.getUploaders();
if (((cup == uploadLimit && coordinator.getInterestedAndChoking() > 0) || cup > uploadLimit || overBWLimitChoke) && !peer.isChoking()) {
// Check if it still wants pieces from us.
if (!peer.isInterested()) {
// so a peer may remain unchoked even if uninterested.
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke uninterested peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.decrementUploaders(false);
// Put it at the back of the list
removed.add(peer);
} else if (overBWLimitChoke) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer);
peer.setChoking(true);
uploaders--;
interestedUploaders--;
coordinator.decrementUploaders(true);
removedCount++;
// Put it at the back of the list for fairness, even though we won't be unchoking this time
removed.add(peer);
} else if (peer.isInteresting() && peer.isChoked()) {
// If they are choking us make someone else a downloader
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke choking peer: " + peer);
peer.setChoking(true);
uploaders--;
interestedUploaders--;
coordinator.decrementUploaders(true);
removedCount++;
// Put it at the back of the list
removed.add(peer);
} else if (!peer.isInteresting() && !coordinator.completed()) {
// If they aren't interesting make someone else a downloader
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke uninteresting peer: " + peer);
peer.setChoking(true);
uploaders--;
interestedUploaders--;
coordinator.decrementUploaders(true);
removedCount++;
// Put it at the back of the list
removed.add(peer);
} else if (peer.isInteresting() && !peer.isChoked() && download == 0) {
// We are downloading but didn't receive anything...
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke downloader that doesn't deliver: " + peer);
peer.setChoking(true);
uploaders--;
interestedUploaders--;
coordinator.decrementUploaders(true);
removedCount++;
// Put it at the back of the list
removed.add(peer);
} else if (peer.isInteresting() && !peer.isChoked() && download < worstdownload) {
// Make sure download is good if we are uploading
worstdownload = download;
worstDownloader = peer;
} else if (upload < worstdownload && coordinator.completed()) {
// Make sure upload is good if we are seeding
worstdownload = upload;
worstDownloader = peer;
}
}
peer.retransmitRequests();
// send PEX, about every 12 minutes
if (((_runCount + i) % 17) == 0 && !peer.isCompleted())
coordinator.sendPeers(peer);
// send Comment Request, about every 30 minutes
if (fetchComments && ((_runCount + i) % 47) == 0)
coordinator.sendCommentReq(peer);
// the inactive checker (above) will eventually disconnect it
if (coordinator.getNeededLength() > 0 || !peer.isCompleted())
peer.keepAlive();
// announce them to local tracker (TrackerClient does this too)
if (dht != null && (_runCount % 5) == 0) {
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash(), peer.isCompleted());
}
}
// for peer
// Resync actual uploaders value
// (can shift a bit by disconnecting peers)
coordinator.setUploaders(uploaders, interestedUploaders);
// Remove the worst downloader if needed. (uploader if seeding)
if (((uploaders == uploadLimit && coordinator.getInterestedAndChoking() > 0) || uploaders > uploadLimit) && worstDownloader != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Choke worst downloader: " + worstDownloader);
worstDownloader.setChoking(true);
coordinator.decrementUploaders(worstDownloader.isInterested());
removedCount++;
// Put it at the back of the list
removed.add(worstDownloader);
}
boolean coordOver = coordinator.overUpBWLimit(uploaded);
synchronized (coordinator.peers) {
if ((!overBWLimit) && !coordOver) {
// Optimistically unchoke a peer
// must be called inside synch
coordinator.unchokePeer();
}
// Put peers back at the end of the list that we removed earlier.
for (Peer peer : removed) {
if (coordinator.peers.remove(peer))
coordinator.peers.add(peer);
}
}
coordinator.addInterestedAndChoking(removedCount);
// store the rates
coordinator.setRateHistory(uploaded, downloaded);
if (_log.shouldLog(Log.DEBUG))
_log.debug("END peers: " + peerList.size() + " uploaders: " + uploaders + " interested: " + interestedUploaders);
// close out unused files, but we don't need to do it every time
Storage storage = coordinator.getStorage();
if (storage != null) {
// The more files a torrent has, the more often we call the cleaner,
// to keep from running out of FDs
int files = storage.getFileCount();
int skip;
if (files == 1)
skip = 6;
else if (files <= 4)
skip = 4;
else if (files <= 20)
skip = 3;
else if (files <= 50)
skip = 2;
else
skip = 1;
if ((_runCount % skip) == 0)
storage.cleanRAFs();
}
// announce ourselves to local tracker (TrackerClient does this too)
if (dht != null && (_runCount % 16) == 0) {
dht.announce(coordinator.getInfoHash(), coordinator.completed());
}
}
Aggregations