📄 trtrackerservertorrentimpl.java
字号:
/*
* File : TRTrackerServerTorrent.java
* Created : 26-Oct-2003
* By : stuff
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.gudy.azureus2.core3.tracker.server.impl;
/**
* @author parg
*
*/
import java.util.*;
import java.io.*;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.tracker.server.*;
import org.gudy.azureus2.core3.util.*;
public class
TRTrackerServerTorrentImpl
implements TRTrackerServerTorrent
{
private static final LogIDs LOGID = LogIDs.TRACKER;
// no point in caching replies smaller than that below
public static final int MIN_CACHE_ENTRY_SIZE = 10;
public static final int MAX_UPLOAD_BYTES_PER_SEC = 3*1024*1024; //3MBs
public static final int MAX_DOWNLOAD_BYTES_PER_SEC = MAX_UPLOAD_BYTES_PER_SEC;
public static final boolean USE_LIGHTWEIGHT_SEEDS = true;
protected HashWrapper hash;
protected Map peer_map = new HashMap();
protected Map peer_reuse_map = new HashMap();
protected List peer_list = new ArrayList();
protected int peer_list_hole_count;
protected boolean peer_list_compaction_suspended;
protected Map lightweight_seed_map = new HashMap();
protected int seed_count;
protected int removed_count;
protected int bad_NAT_count; // calculated periodically
protected Random random = new Random( SystemTime.getCurrentTime());
protected long last_scrape_calc_time;
protected Map last_scrape;
protected LinkedHashMap announce_cache = new LinkedHashMap();
protected TRTrackerServerTorrentStatsImpl stats;
protected List listeners = new ArrayList();
protected boolean deleted;
protected boolean map_size_diff_reported;
protected byte duplicate_peer_checker_index = 0;
protected byte[] duplicate_peer_checker = new byte[0];
protected boolean caching_enabled = true;
protected AEMonitor this_mon = new AEMonitor( "TRTrackerServerTorrent" );
public
TRTrackerServerTorrentImpl(
HashWrapper _hash )
{
hash = _hash;
stats = new TRTrackerServerTorrentStatsImpl( this );
}
public TRTrackerServerPeerImpl
peerContact(
String event,
HashWrapper peer_id,
int port,
String ip_address,
boolean loopback,
String tracker_key,
long uploaded,
long downloaded,
long left,
long interval_requested )
throws TRTrackerServerException
{
try{
this_mon.enter();
// System.out.println( "TRTrackerServerTorrent: peerContact, ip = " + ip_address );
boolean stopped = event != null && event.equalsIgnoreCase("stopped");
boolean completed = event != null && event.equalsIgnoreCase("completed");
long now = SystemTime.getCurrentTime();
int tracker_key_hash_code = tracker_key==null?0:tracker_key.hashCode();
TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_map.get( peer_id );
boolean new_peer = false;
boolean already_completed = false;
long last_contact_time = 0;
long ul_diff = 0;
long dl_diff = 0;
long le_diff = 0;
byte[] ip_address_bytes = ip_address.getBytes( Constants.BYTE_ENCODING );
if ( peer == null ){
String reuse_key = new String( ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + port;
byte last_NAT_status = loopback?TRTrackerServerPeer.NAT_CHECK_OK:TRTrackerServerPeer.NAT_CHECK_UNKNOWN;
new_peer = true;
// check to see if this peer already has an entry against this torrent
// and if so delete it (assumption is that the client has quit and
// restarted with new peer id
//System.out.println( "new peer" );
TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( reuse_key );
if ( old_peer != null ){
last_contact_time = old_peer.getLastContactTime();
already_completed = old_peer.getDownloadCompleted();
removePeer( old_peer );
lightweight_seed_map.remove( old_peer.getPeerId());
}else{
lightweightSeed lws = (lightweightSeed)lightweight_seed_map.remove( peer_id );
if ( lws != null ){
last_contact_time = lws.getLastContactTime();
ul_diff = uploaded - lws.getUploaded();
if ( ul_diff < 0 ){
ul_diff = 0;
}
last_NAT_status = lws.getNATStatus();
}else{
last_contact_time = now;
}
}
if ( !stopped ){
peer = new TRTrackerServerPeerImpl(
peer_id,
tracker_key_hash_code,
ip_address_bytes,
port,
last_contact_time,
already_completed,
last_NAT_status );
peer_map.put( peer_id, peer );
peer_list.add( peer );
peer_reuse_map.put( reuse_key, peer );
}
}else{
int existing_tracker_key_hash_code = peer.getKeyHashCode();
// System.out.println( "tracker_key:" + existing_tracker_key + "/" + tracker_key );
if ( existing_tracker_key_hash_code != tracker_key_hash_code ){
throw( new TRTrackerServerException( "Unauthorised: key mismatch "));
}
already_completed = peer.getDownloadCompleted();
last_contact_time = peer.getLastContactTime();
if ( stopped ){
removePeer( peer );
}else{
// IP may have changed - update if required
// it is possible for two az clients to have the same peer id. Unlikely but possible
// or indeed some hacked versions could do it on purpose. If this is the case then all we
// will see here is address/port changes as each peer announces
byte[] old_ip = peer.getIPAsRead();
int old_port = peer.getPort();
if ( peer.checkForIPOrPortChange( ip_address_bytes, port )){
// same peer id so same port
String old_key = new String( old_ip, Constants.BYTE_ENCODING ) + ":" + old_port;
String new_key = new String(ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + port;
// it is possible, on address change, that the target address already exists and is
// (was) being used by another peer. Given that this peer has taken over its address
// the assumption is that the other peer has also had an address change and has yet
// to report it. The only action here is to delete the other peer
TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( new_key );
if ( old_peer != null ){
removePeer( old_peer );
}
// now swap the keys
if ( peer_reuse_map.remove( old_key ) == null ){
Debug.out( "TRTrackerServerTorrent: IP address change: '" + old_key + "' -> '" + new_key + "': old key not found" );
}
peer_reuse_map.put( new_key, peer );
}
}
}
// a null peer here signifies a new peer whose first state was "stopped"
long new_timeout = now + ( interval_requested * 1000 * TRTrackerServerImpl.CLIENT_TIMEOUT_MULTIPLIER );
if ( peer != null ){
peer.setTimeout( now, new_timeout );
// if this is the first time we've heard from this peer then we don't want to
// use existing ul/dl value diffs as they will have been reported previously
// (either the client's changed peer id by stop/start (in which case the values
// should be 0 anyway as its a per-session total), or the tracker's been
// stopped and started).
if ( !new_peer ){
ul_diff = uploaded - peer.getUploaded();
dl_diff = downloaded - peer.getDownloaded();
}
// simple rate control
long elapsed_time = now - last_contact_time;
if ( elapsed_time == 0 ){
elapsed_time = SystemTime.TIME_GRANULARITY_MILLIS;
}
long ul_rate = (ul_diff*1000)/elapsed_time; // bytes per second
long dl_rate = (dl_diff*1000)/elapsed_time;
if ( ul_rate > MAX_UPLOAD_BYTES_PER_SEC ){
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer "
+ peer.getIPRaw() + "/"
+ new String(peer.getPeerId().getHash())
+ " reported an upload rate of " + ul_rate / 1024
+ " KiB/s per second"));
ul_diff = 0;
}
if ( dl_rate > MAX_DOWNLOAD_BYTES_PER_SEC ){
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer "
+ peer.getIPRaw() + "/"
+ new String(peer.getPeerId().getHash())
+ " reported a download rate of " + dl_rate / 1024
+ " KiB/s per second"));
dl_diff = 0;
}
// when the peer is removed its "left" amount will dealt with
le_diff = stopped?0:(left - peer.getAmountLeft());
boolean was_seed = new_peer?false:peer.isSeed();
peer.setStats( uploaded, downloaded, left );
boolean is_seed = peer.isSeed();
if (!(stopped || was_seed || !is_seed )){
seed_count++;
}
}
stats.addAnnounce( ul_diff, dl_diff, le_diff );
if ( completed && !already_completed ){
peer.setDownloadCompleted();
stats.addCompleted();
}
if ( peer != null && peer.isSeed()){
int seed_limit = TRTrackerServerImpl.getSeedLimit();
if ( seed_limit != 0 && seed_count > seed_limit && !loopback ){
removePeer( peer );
// this is picked up by AZ client removal rules and causes the torrent to
// be removed
throw( new TRTrackerServerException( "too many seeds" ));
}
int seed_retention = TRTrackerServerImpl.getMaxSeedRetention();
if ( seed_retention != 0 && seed_count > seed_retention ){
// remove 5% of the seeds
int to_remove = (seed_retention/20)+1;
try{
peer_list_compaction_suspended = true;
// remove bad NAT ones in preference to others
for (int bad_nat_loop=TRTrackerServerNATChecker.getSingleton().isEnabled()?0:1;bad_nat_loop<2;bad_nat_loop++){
for (int i=0;i<peer_list.size();i++){
TRTrackerServerPeerImpl this_peer = (TRTrackerServerPeerImpl)peer_list.get(i);
if ( this_peer != null && this_peer.isSeed()){
boolean bad_nat = this_peer.isNATStatusBad();
if ( ( bad_nat_loop == 0 && bad_nat ) ||
( bad_nat_loop == 1 )){
if ( USE_LIGHTWEIGHT_SEEDS ){
lightweight_seed_map.put(
this_peer.getPeerId(),
new lightweightSeed(
now,
new_timeout,
this_peer.getUploaded(),
this_peer.getNATStatus()));
}
removePeer( this_peer, i );
if ( --to_remove == 0 ){
break;
}
}
}
}
if ( to_remove == 0 ){
break;
}
}
}finally{
peer_list_compaction_suspended = false;
}
checkForPeerListCompaction( false );
}
}
return( peer );
}catch( UnsupportedEncodingException e ){
throw( new TRTrackerServerException( "Encoding fails", e ));
}finally{
this_mon.exit();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -