📄 trtrackerclientclassicimpl.java
字号:
/*
* File : TRTrackerClientClassicImpl.java
* Created : 5 Oct. 2003
* By : Parg
*
* 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.client.classic;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import javax.net.ssl.*;
import java.util.zip.*;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.config.*;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.security.*;
import org.gudy.azureus2.core3.tracker.client.*;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.core3.internat.*;
import org.gudy.azureus2.core3.peer.util.*;
import org.gudy.azureus2.core3.peer.PEPeerServer;
import org.gudy.azureus2.core3.peer.PEPeerServerListener;
import org.gudy.azureus2.core3.tracker.protocol.*;
import org.gudy.azureus2.core3.tracker.protocol.udp.*;
import org.gudy.azureus2.core3.tracker.util.impl.*;
import com.aelitis.azureus.core.proxy.AEProxyFactory;
/**
*
* This class handles communication with the tracker
*
* @author Olivier
*
*/
public class
TRTrackerClientClassicImpl
implements TRTrackerClient, PEPeerServerListener, ParameterListener
{
private static final int OVERRIDE_PERIOD = 10*1000;
private static Timer tracker_timer = new Timer( "Tracker Timer", 32);
public static String UDP_REALM = "UDP Tracker";
private TOTorrent torrent;
private PEPeerServer peer_server;
private TimerEvent current_timer_event;
private TimerEventPerformer timer_event_action;
private int tracker_state = TS_INITIALISED;
private String tracker_status_str = "";
private TRTrackerResponse last_response = null;
private long last_update_time_secs;
private long current_time_to_wait_secs;
private int failure_added_time = 0;
private long failure_time_last_updated = 0;
private boolean stopped;
private boolean completed;
private boolean complete_reported = false;
private boolean update_in_progress = false;
private long rd_last_override = 0;
private int rd_override_percentage = 100;
private List trackerUrlLists;
private URL lastUsedUrl;
private String trackerUrlListString;
private byte[] torrent_hash;
private PeerIdentityDataID peer_data_id;
private String info_hash = "info_hash=";
private byte[] tracker_peer_id;
private String tracker_peer_id_str = "&peer_id=";
private byte[] data_peer_id;
private String key_id = "";
private static final int key_id_length = 8;
private int key_udp;
private String port;
private String ip_override;
private String last_warning_message = "";
private TrackerClientAnnounceDataProvider announce_data_provider;
private Map tracker_peer_cache = new LinkedHashMap(); // insertion order - most recent at end
private AEMonitor tracker_peer_cache_mon = new AEMonitor( "TRTrackerClientClassic:PC" );
protected AEMonitor this_mon = new AEMonitor( "TRTrackerClientClassic" );
private static final boolean socks_peer_inform;
private boolean destroyed;
static{
socks_peer_inform =
COConfigurationManager.getBooleanParameter("Proxy.Data.Enable", false)&&
COConfigurationManager.getBooleanParameter("Proxy.Data.SOCKS.inform", true );
}
public final static int componentID = 2;
public final static int evtLifeCycle = 0;
public final static int evtFullTrace = 1;
public final static int evtErrors = 2;
// listener
private static final int LDT_TRACKER_RESPONSE = 1;
private static final int LDT_URL_CHANGED = 2;
private static final int LDT_URL_REFRESH = 3;
private ListenerManager listeners = ListenerManager.createManager(
"TrackerClient:ListenDispatcher",
new ListenerManagerDispatcher()
{
public void
dispatch(
Object _listener,
int type,
Object value )
{
TRTrackerClientListener listener = (TRTrackerClientListener)_listener;
if ( type == LDT_TRACKER_RESPONSE ){
listener.receivedTrackerResponse((TRTrackerResponse)value);
}else if ( type == LDT_URL_CHANGED ){
Object[] x = (Object[])value;
String url = (String)x[0];
boolean explicit = ((Boolean)x[1]).booleanValue();
listener.urlChanged(url, explicit );
}else{
listener.urlRefresh();
}
}
});
static final String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public static byte[]
createPeerID()
{
byte[] peerId = new byte[20];
byte[] version = Constants.VERSION_ID;
for (int i = 0; i < 8; i++) {
peerId[i] = version[i];
}
for (int i = 8; i < 20; i++) {
int pos = (int) ( Math.random() * chars.length());
peerId[i] = (byte)chars.charAt(pos);
}
// System.out.println( "generated new peer id:" + ByteFormatter.nicePrint(peerId));
return( peerId );
}
public static String
createKeyID()
{
String key_id = "";
for (int i = 0; i < key_id_length; i++) {
int pos = (int) ( Math.random() * chars.length());
key_id += chars.charAt(pos);
}
return( key_id );
}
public
TRTrackerClientClassicImpl(
TOTorrent _torrent,
PEPeerServer _peer_server )
throws TRTrackerClientException
{
torrent = _torrent;
peer_server = _peer_server;
//Get the Tracker url
constructTrackerUrlLists( true );
//Create our unique peerId
tracker_peer_id = createPeerID();
if ( COConfigurationManager.getBooleanParameter("Tracker Separate Peer IDs", false)){
data_peer_id = createPeerID();
}else{
data_peer_id = tracker_peer_id;
}
key_id = createKeyID();
key_udp = (int)(Math.random() * (double)0xFFFFFFFFL );
try {
torrent_hash = _torrent.getHash();
peer_data_id = PeerIdentityManager.createDataID( torrent_hash );
this.info_hash += URLEncoder.encode(new String(torrent_hash, Constants.BYTE_ENCODING), Constants.BYTE_ENCODING).replaceAll("\\+", "%20");
this.tracker_peer_id_str += URLEncoder.encode(new String(tracker_peer_id, Constants.BYTE_ENCODING), Constants.BYTE_ENCODING).replaceAll("\\+", "%20");
}catch (UnsupportedEncodingException e){
LGLogger.log(componentID, evtLifeCycle,"URL encode fails", e );
throw( new TRTrackerClientException( "TRTrackerClient: URL encode fails"));
}catch( TOTorrentException e ){
LGLogger.log(componentID, evtLifeCycle,"Torrent hash retrieval fails", e );
throw( new TRTrackerClientException( "TRTrackerClient: URL encode fails"));
}
peer_server.addListener( this );
COConfigurationManager.addParameterListener("TCP.Announce.Port",this);
setPort();
timer_event_action =
new TimerEventPerformer()
{
public void
perform(
TimerEvent this_event )
{
long secs_to_wait = getErrorRetryInterval();
try{
secs_to_wait = requestUpdateSupport();
}finally{
current_time_to_wait_secs = secs_to_wait;
if ( tracker_state == TS_STOPPED ){
// System.out.println( "\tperform: stopped so no more events");
}else{
try{
this_mon.enter();
// it is possible that the current event was being processed
// when another thread cancelled it and created a further timer
// event. if this is the case we don't want to go ahead and
// create another timer as one already exists
if ( this_event.isCancelled()){
// System.out.println( "\tperform: cancelled so no new event");
}else{
secs_to_wait = getAdjustedSecsToWait();
long target_time = SystemTime.getCurrentTime() + (secs_to_wait*1000);
if ( current_timer_event != null && !current_timer_event.isCancelled()){
if ( current_timer_event != this_event &&
current_timer_event.getWhen() < target_time ){
// existing event is earlier then current target, use it
return;
}
current_timer_event.cancel();
}
if ( !destroyed ){
current_timer_event =
tracker_timer.addEvent( target_time, this );
}
}
}finally{
this_mon.exit();
}
}
}
}
};
LGLogger.log(componentID, evtLifeCycle, LGLogger.INFORMATION, "Tracker Client Created using url : " + trackerUrlListString);
}
public void
portChanged(
int new_port )
{
setPort();
update( true );
}
protected void
setPort()
{
// we currently don't support incoming connections when SOCKs proxying
int port_num;
if ( socks_peer_inform ){
port_num = 0;
}else{
port_num = peer_server.getPort();
}
String portOverride = COConfigurationManager.getStringParameter("TCP.Announce.Port","");
if(! portOverride.equals("")) {
port = "&port=" + portOverride;
} else {
port = "&port=" + port_num;
// BitComet extension for no incoming connections
if ( port_num == 0 ){
port += "&hide=1";
}
}
}
protected long
getAdjustedSecsToWait()
{
long secs_to_wait = current_time_to_wait_secs;
if( last_response != null && last_response.getStatus() != TRTrackerResponse.ST_ONLINE ) {
secs_to_wait = getErrorRetryInterval();
}
else{
if( rd_override_percentage == 0 ) return REFRESH_MINIMUM_SECS;
secs_to_wait = (secs_to_wait * rd_override_percentage) /100;
if ( secs_to_wait < REFRESH_MINIMUM_SECS ){
secs_to_wait = REFRESH_MINIMUM_SECS;
}
}
return( secs_to_wait );
}
public int
getStatus()
{
return( tracker_state );
}
public String
getStatusString()
{
return( tracker_status_str );
}
public void
setRefreshDelayOverrides(
int percentage )
{
if ( percentage > 100 ){
percentage = 100;
}else if ( percentage < 0 ){
percentage = 0;
}
long now = SystemTime.getCurrentTime();
//only start overriding once the tracker announce update has been called
boolean override_allowed = rd_last_override > 0 && now - rd_last_override > OVERRIDE_PERIOD;
if ( ( SystemTime.isErrorLast10sec() || override_allowed ) && rd_override_percentage != percentage ){
try{
this_mon.enter();
rd_last_override = now;
rd_override_percentage = percentage;
if ( current_timer_event != null && !current_timer_event.isCancelled()){
long start = current_timer_event.getCreatedTime();
long expiry = current_timer_event.getWhen();
long secs_to_wait = getAdjustedSecsToWait();
long target_time = start + (secs_to_wait*1000);
if ( target_time != expiry ){
current_timer_event.cancel();
if ( !destroyed ){
current_timer_event =
tracker_timer.addEvent(
start,
target_time,
timer_event_action );
}
}
}
}finally{
this_mon.exit();
}
}
}
public int
getTimeUntilNextUpdate()
{
try{
this_mon.enter();
if ( current_timer_event == null ){
return( getErrorRetryInterval() );
}
int rem = (int)((current_timer_event.getWhen() - SystemTime.getCurrentTime())/1000);
return( rem );
}finally{
this_mon.exit();
}
}
public int
getLastUpdateTime()
{
return( (int)last_update_time_secs );
}
public void
update(
boolean force )
{
long time = SystemTime.getCurrentTime() / 1000;
if ( SystemTime.isErrorLast1min() || force ||
( time - last_update_time_secs >= REFRESH_MINIMUM_SECS )){
requestUpdate();
}
}
public void
complete(
boolean already_reported )
{
complete_reported = (complete_reported || already_reported );
completed = true;
requestUpdate();
}
public void
stop()
{
stopped = true;
requestUpdate();
}
protected void
requestUpdate()
{
try{
this_mon.enter();
if ( current_timer_event != null ){
current_timer_event.cancel();
}
rd_last_override = SystemTime.getCurrentTime(); //"pause" overrides for 10s
if ( !destroyed ){
current_timer_event =
tracker_timer.addEvent(
SystemTime.getCurrentTime(),
timer_event_action );
}
}
finally{
this_mon.exit();
}
}
protected long
requestUpdateSupport()
{
boolean clear_progress = true;
try{
try{
this_mon.enter();
if ( update_in_progress ){
clear_progress = false;
return( getErrorRetryInterval() );
}
update_in_progress = true;
}finally{
this_mon.exit();
}
last_update_time_secs = SystemTime.getCurrentTime()/1000;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -