⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 trtrackerservertorrentimpl.java

📁 这是一个基于java编写的torrent的P2P源码
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
 * 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 java.net.URL;
import java.net.UnknownHostException;

import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.tracker.server.*;
import org.gudy.azureus2.core3.util.*;

import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition;


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;

	public static final int		MAX_IP_OVERRIDE_PEERS	= 64;
		
	public static final byte	COMPACT_MODE_NONE		= 0;
	public static final byte	COMPACT_MODE_NORMAL		= 1;
	public static final byte	COMPACT_MODE_AZ			= 2;
	public static final byte	COMPACT_MODE_AZ_2		= 3;
	
	private static final int	QUEUED_PEERS_MAX_SWARM_SIZE	= 32;
	private static final int	QUEUED_PEERS_MAX			= 32;
	private static final int	QUEUED_PEERS_ADD_MAX		= 3;
		
		
	private TRTrackerServerImpl	server;
	private HashWrapper			hash;

	private Map				peer_map 		= new HashMap();
	private Map				peer_reuse_map	= new HashMap();
	
	private List			peer_list		= new ArrayList();
	private int				peer_list_hole_count;
	private boolean			peer_list_compaction_suspended;
	
	private List			biased_peers			= null;
	
	private Map				lightweight_seed_map	= new HashMap();
	
	private int				seed_count;
	private int				removed_count;
	
	private int				ip_override_count;
	
	private int				bad_NAT_count;	// calculated periodically
	
	private Random			random		= new Random( SystemTime.getCurrentTime());
	
	private long			last_scrape_calc_time;
	private Map				last_scrape;
	
	private LinkedHashMap		announce_cache	= new LinkedHashMap();
	
	private TRTrackerServerTorrentStatsImpl	stats;
		
	private List			listeners	= new ArrayList();
	private List			peer_listeners;
	private boolean			deleted;
	private boolean			enabled;
	
	private boolean			map_size_diff_reported;
	private boolean			ip_override_limit_exceeded_reported;
	
	private byte			duplicate_peer_checker_index	= 0;
	private byte[]			duplicate_peer_checker			= new byte[0];
	
	private URL[]			redirects;
	
	private boolean			caching_enabled	= true;
	
	private LinkedList		queued_peers;
	
	protected AEMonitor this_mon 	= new AEMonitor( "TRTrackerServerTorrent" );

	public
	TRTrackerServerTorrentImpl(
		TRTrackerServerImpl		_server,
		HashWrapper				_hash,
		boolean					_enabled )
	{
		server		= _server;
		hash		= _hash;
		enabled		= _enabled;
		
		stats		= new TRTrackerServerTorrentStatsImpl( this );
	}
	
	public void
	setEnabled(
		boolean		_enabled )
	{
		enabled	= _enabled;
	}
	
	public boolean
	isEnabled()
	{
		return( enabled );
	}
	
	public TRTrackerServerPeerImpl
	peerContact(
		String				url_parameters,
		String				event,
		HashWrapper			peer_id,
		int					tcp_port,
		int					udp_port,
		int					http_port,
		byte				crypto_level,
		byte				az_ver,
		String				ip_address,
		boolean				ip_override,
		boolean				loopback,
		String				tracker_key,
		long				uploaded,
		long				downloaded,
		long				left,
		long				interval_requested,
		int					up_speed,
		DHTNetworkPosition	network_position )
	
		throws TRTrackerServerException
	{
		if ( !enabled ){
			
			throw( new TRTrackerServerException( "Torrent temporarily disabled" ));
		}
		
			// we can safely resolve the client_ip_address here as it is either already resolved or that of
			// the tracker. We need it resolved so we canonically store peers, otherwise we can get two 
			// entries for a dns name and the corresponding ip

		if ( !HostNameToIPResolver.isNonDNSName( ip_address )){
		
			try{
				ip_address	= HostNameToIPResolver.syncResolve( ip_address ).getHostAddress();
			
			}catch( UnknownHostException e ){
				
				Debug.printStackTrace( e );
			}
		}
		
		TRTrackerServerException	deferred_failure = null;
		
		try{
			this_mon.enter();
		
			handleRedirects( url_parameters, ip_address, false  );

			// System.out.println( "TRTrackerServerTorrent: peerContact, ip = " + ip_address );
				
			int	event_type = TRTrackerServerTorrentPeerListener.ET_UPDATED;
			
			if ( event != null && event.length() > 2){
		
				char	c = event.charAt(2);
				
				if ( c == 'm' ){	// "coMpleted"
					
					event_type	= TRTrackerServerTorrentPeerListener.ET_COMPLETE;
					
				}else if ( c == 'o' ){	// "stOpped"
					
					event_type = TRTrackerServerTorrentPeerListener.ET_STOPPED;
					
				}else{
					
					event_type = TRTrackerServerTorrentPeerListener.ET_STARTED;
				}
			}
			
			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		peer_already_removed	= 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 ) + ":" + tcp_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 ){
							
						// don't allow an ip_override to grab a non-override entry as this is a way for 
						// a malicious client to remove, say, a seed (simply send in a "stopped" command
						// with override set to the seed you want to remove)
					
					if ( ip_override && !old_peer.isIPOverride()){
						
						throw( new TRTrackerServerException( "IP Override denied" ));
					}
					
					last_contact_time	= old_peer.getLastContactTime();
					
					already_completed	= old_peer.getDownloadCompleted();
					
					removePeer( old_peer,  TRTrackerServerTorrentPeerListener.ET_REPLACED, null );
					
					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 ( event_type != TRTrackerServerTorrentPeerListener.ET_STOPPED ){			
						
					if ( ip_override && ip_override_count >= MAX_IP_OVERRIDE_PEERS && !loopback ){
					
							// bail out - the peer will still get an announce response but we don't
							// want too many override peers on a torrent as these can be spoofed
							// to cause trouble
						
						if ( !ip_override_limit_exceeded_reported ){
							
							ip_override_limit_exceeded_reported	= true;
							
							Debug.out( "Too many ip-override peers for " + ByteFormatter.encodeString( hash.getBytes()));
						}
						
						return( null );
					}
					
					peer = new TRTrackerServerPeerImpl( 
									peer_id, 
									tracker_key_hash_code, 
									ip_address_bytes,
									ip_override,
									tcp_port,
									udp_port,
									http_port,
									crypto_level,
									az_ver,
									last_contact_time,
									already_completed,
									last_NAT_status,
									up_speed,
									network_position );
					
					if ( ip_override ){
						
						ip_override_count++;
					}
					
					peer_map.put( peer_id, peer );
					
					peer_list.add( peer );
									
					peer_reuse_map.put( reuse_key, peer );
					
					Set	b = server.getBiasedPeers();
					
					if ( b != null && b.contains( peer.getIPRaw())){
							
						peer.setBiased( true );
						
						if ( biased_peers == null ){
						
							biased_peers = new ArrayList();
						}
						
						biased_peers.add( peer );
					}
					
					if ( queued_peers != null ){
						
						if ( peer_map.size() > QUEUED_PEERS_MAX_SWARM_SIZE ){
						
							queued_peers = null;
							
						}else{
					
								// peer has become active, remove the queued peer info
							
							Iterator	it = queued_peers.iterator();
							
							while( it.hasNext()){
									
								QueuedPeer	qp = (QueuedPeer)it.next();
									
								if ( qp.sameAs( peer )){
										
									it.remove();
										
									break;
								}
							}
						}
					}
				}
			}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 "));
					
				}
				
					// prevent an ip_override peer from affecting a non-override entry
				
				if ( ip_override && !peer.isIPOverride()){
					
					throw( new TRTrackerServerException( "IP Override denied" ));
				}

				already_completed	= peer.getDownloadCompleted();
				
				last_contact_time	= peer.getLastContactTime();
				
				if ( event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED ){
					
					removePeer( peer, event_type, url_parameters );
					
					peer_already_removed	= true;
					
				}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.getTCPPort();
					
					if ( peer.update( ip_address_bytes, tcp_port, udp_port, http_port, crypto_level, az_ver, up_speed, network_position )){
						
							// 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 ) + ":" + tcp_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, TRTrackerServerTorrentPeerListener.ET_REPLACED, null );
						}
	
							// 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 ){
				
					// if the peer has already been removed above then it will have reported the
					// event already
				
				if ( !peer_already_removed ){
					
					try{
						peerEvent( peer, event_type, url_parameters );
						
					}catch( TRTrackerServerException	e ){
						
						deferred_failure = e;
					}
				}

				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 = (event_type==TRTrackerServerTorrentPeerListener.ET_STOPPED)?0:(left - peer.getAmountLeft());
				
				boolean	was_seed 	= new_peer?false:peer.isSeed();
				
				peer.setStats( uploaded, downloaded, left );
				
				boolean	is_seed		= peer.isSeed();
				
				if (!(event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED || was_seed || !is_seed )){
					
					seed_count++;
				}
			}
			
			stats.addAnnounce( ul_diff, dl_diff, le_diff, peer != null && peer.isBiased());
			
			if ( event_type==TRTrackerServerTorrentPeerListener.ET_COMPLETE && !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 ){
					
					if ( !peer_already_removed ){
						
						removePeer( peer, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null );
					}
					
						// 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, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null );
			
										if ( --to_remove == 0 ){
											
											break;
										}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -