📄 trtrackerbtannouncerimpl.java
字号:
/*
* File : TRTrackerBTAnnouncerImpl.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.impl.bt;
import java.io.*;
import java.net.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Random;
import java.util.Properties;
import java.util.StringTokenizer;
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.tracker.client.impl.*;
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.*;
import org.gudy.azureus2.core3.tracker.protocol.*;
import org.gudy.azureus2.core3.tracker.protocol.udp.*;
import org.gudy.azureus2.core3.tracker.util.TRTrackerUtils;
import org.gudy.azureus2.plugins.clientid.*;
import org.gudy.azureus2.plugins.download.*;
import org.gudy.azureus2.pluginsimpl.local.clientid.ClientIDManagerImpl;
import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition;
import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPositionManager;
import com.aelitis.azureus.core.networkmanager.impl.udp.UDPNetworkManager;
import com.aelitis.net.udp.uc.PRUDPPacket;
import com.aelitis.net.udp.uc.PRUDPPacketHandler;
import com.aelitis.net.udp.uc.PRUDPPacketHandlerException;
import com.aelitis.net.udp.uc.PRUDPPacketHandlerFactory;
import com.aelitis.net.udp.uc.PRUDPPacketRequest;
/**
*
* This class handles communication with the tracker
*
* @author Olivier
*
*/
public class
TRTrackerBTAnnouncerImpl
extends TRTrackerAnnouncerImpl
{
private static final int OVERRIDE_PERIOD = 10*1000;
protected static Timer tracker_timer = new Timer( "Tracker Timer", 32);
public static String UDP_REALM = "UDP Tracker";
static{
PRUDPTrackerCodecs.registerCodecs();
}
private static AEMonitor class_mon = new AEMonitor( "TRTrackerBTAnnouncer:class" );
private static Map tracker_report_map = new HashMap();
private TOTorrent torrent;
private TimerEvent current_timer_event;
private TimerEventPerformer timer_event_action;
protected int tracker_state = TS_INITIALISED;
private String tracker_status_str = "";
private TRTrackerAnnouncerResponse last_response = null;
private long last_update_time_secs;
private long current_time_to_wait_secs;
private boolean manual_control;
private long min_interval = 0;
private int failure_added_time = 0;
private long failure_time_last_updated = 0;
private boolean stopped;
private boolean stopped_for_queue;
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 long min_interval_override = 0;
private List trackerUrlLists;
private URL lastUsedUrl;
private URL lastAZTrackerCheckedURL;
private HashWrapper torrent_hash;
private String last_tracker_message; // per torrent memory
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 tracker_id = "";
private String ip_override;
private String[] peer_networks;
private TRTrackerAnnouncerDataProvider announce_data_provider;
protected AEMonitor this_mon = new AEMonitor( "TRTrackerBTAnnouncer" );
private boolean az_tracker;
private boolean destroyed;
static final String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
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
TRTrackerBTAnnouncerImpl(
TOTorrent _torrent,
String[] _peer_networks,
boolean _manual )
throws TRTrackerAnnouncerException
{
torrent = _torrent;
peer_networks = _peer_networks;
manual_control = _manual;
//Get the Tracker url
constructTrackerUrlLists( true );
//Create our unique peerId
try{
tracker_peer_id = ClientIDManagerImpl.getSingleton().generatePeerID( torrent, true );
if ( COConfigurationManager.getBooleanParameter("Tracker Separate Peer IDs", false)){
data_peer_id = ClientIDManagerImpl.getSingleton().generatePeerID( torrent, false );
}else{
data_peer_id = tracker_peer_id;
}
}catch( ClientIDException e ){
throw( new TRTrackerAnnouncerException( "TRTrackerAnnouncer: Peer ID generation fails", e ));
}
key_id = createKeyID();
key_udp = (int)(Math.random() * 0xFFFFFFFFL );
try {
torrent_hash = _torrent.getHashWrapper();
this.info_hash += URLEncoder.encode(new String(torrent_hash.getBytes(), 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){
Logger.log(new LogEvent(torrent, LOGID, "URL encode fails", e));
throw( new TRTrackerAnnouncerException( "TRTrackerAnnouncer: URL encode fails"));
}catch( TOTorrentException e ){
Logger.log(new LogEvent(torrent, LOGID, "Torrent hash retrieval fails", e));
throw( new TRTrackerAnnouncerException( "TRTrackerAnnouncer: URL encode fails"));
}
timer_event_action =
new TimerEventPerformer()
{
public void
perform(
TimerEvent this_event )
{
if ( manual_control ){
requestUpdateSupport();
return;
}
long secs_to_wait = getErrorRetryInterval();
try{
secs_to_wait = requestUpdateSupport();
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"Next tracker announce (unadjusted) will be in " + secs_to_wait
+ "s"));
}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();
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"Next tracker announce (adjusted) will be in "
+ secs_to_wait + "s"));
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();
}
}
}
}
};
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"Tracker Announcer Created using url : " + trackerURLListToString()));
}
protected long
getAdjustedSecsToWait()
{
long secs_to_wait = current_time_to_wait_secs;
if( last_response != null && last_response.getStatus() != TRTrackerAnnouncerResponse.ST_ONLINE ) {
if( last_response.getStatus() == TRTrackerAnnouncerResponse.ST_REPORTED_ERROR ) {
//the tracker has explicitly reported an error (torrent is unauthorized for example),
//so there's no need to keep trying to re-announce as if it were actually offline
//there's no "min interval" returned, so start the re-announce backoff timings at 15min
if( failure_added_time < 900 ) failure_added_time = 900;
secs_to_wait = getErrorRetryInterval();
if (Logger.isEnabled()) {
Logger.log(new LogEvent(torrent, LOGID,
"MIN INTERVAL CALC: tracker reported error, " +
"adjusting to error retry interval"));
}
}
else { //tracker is OFFLINE
secs_to_wait = getErrorRetryInterval();
if (Logger.isEnabled()) {
Logger.log(new LogEvent(torrent, LOGID,
"MIN INTERVAL CALC: tracker seems to be offline, " +
"adjusting to error retry interval"));
}
}
}
else{
if( rd_override_percentage == 0 ) {
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"MIN INTERVAL CALC: override, perc = 0"));
return REFRESH_MINIMUM_SECS;
}
if (rd_override_percentage != 100) {
secs_to_wait = (secs_to_wait * rd_override_percentage) /100;
if (Logger.isEnabled()) {
Logger.log(new LogEvent(torrent, LOGID,
"MIN INTERVAL CALC: override, perc = " + rd_override_percentage));
}
}
if ( secs_to_wait < REFRESH_MINIMUM_SECS ){
secs_to_wait = REFRESH_MINIMUM_SECS;
}
//use 'min interval' for calculation
if( min_interval != 0 && secs_to_wait < min_interval ) {
float percentage = (float)min_interval / current_time_to_wait_secs; //percentage of original interval
//long orig_override = secs_to_wait;
int added_secs = (int)((min_interval - secs_to_wait) * percentage); //increase by x percentage of difference
secs_to_wait += added_secs;
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"MIN INTERVAL CALC: min_interval=" + min_interval + ", interval="
+ current_time_to_wait_secs + ", orig=" + current_time_to_wait_secs
+ ", new=" + secs_to_wait + ", added=" + added_secs
+ ", perc=" + percentage));
}
}
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( now < rd_last_override ) override_allowed = true; //time went backwards
if ( 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 ){
if (Logger.isEnabled())
Logger.log(new LogEvent(torrent, LOGID,
"Changed next tracker announce to " + secs_to_wait
+ "s via " + Debug.getStackTrace(true, false, 0, 3)));
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 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -