📄 dhtnatpuncherimpl.java
字号:
/*
* Created on 11-Aug-2005
* Created by Paul Gardner
* Copyright (C) 2005, 2006 Aelitis, All Rights Reserved.
*
* 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, or (at your option) any later version.
* 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.
* 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.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.dht.nat.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.util.*;
import org.gudy.azureus2.core3.util.AESemaphore;
import org.gudy.azureus2.core3.util.AEThread;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.SHA1Simple;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.utils.*;
import com.aelitis.azureus.core.dht.DHT;
import com.aelitis.azureus.core.dht.DHTLogger;
import com.aelitis.azureus.core.dht.DHTOperationAdapter;
import com.aelitis.azureus.core.dht.DHTOperationListener;
import com.aelitis.azureus.core.dht.nat.*;
import com.aelitis.azureus.core.dht.transport.DHTTransport;
import com.aelitis.azureus.core.dht.transport.DHTTransportContact;
import com.aelitis.azureus.core.dht.transport.DHTTransportException;
import com.aelitis.azureus.core.dht.transport.DHTTransportListener;
import com.aelitis.azureus.core.dht.transport.DHTTransportProgressListener;
import com.aelitis.azureus.core.dht.transport.DHTTransportReplyHandlerAdapter;
import com.aelitis.azureus.core.dht.transport.DHTTransportTransferHandler;
import com.aelitis.azureus.core.dht.transport.DHTTransportValue;
import com.aelitis.azureus.core.dht.transport.udp.DHTTransportUDP;
import com.aelitis.azureus.core.dht.transport.udp.DHTTransportUDPContact;
public class
DHTNATPuncherImpl
implements DHTNATPuncher
{
private static boolean TESTING = false;
private static boolean TRACE = false;
static{
if ( TESTING ){
System.out.println( "**** DHTNATPuncher test on ****" );
}
if ( TRACE ){
System.out.println( "**** DHTNATPuncher trace on ****" );
}
}
private static final int RT_BIND_REQUEST = 0;
private static final int RT_BIND_REPLY = 1;
private static final int RT_PUNCH_REQUEST = 2;
private static final int RT_PUNCH_REPLY = 3;
private static final int RT_CONNECT_REQUEST = 4;
private static final int RT_CONNECT_REPLY = 5;
private static final int RT_TUNNEL_INBOUND = 6;
private static final int RT_TUNNEL_OUTBOUND = 7;
private static final int RT_QUERY_REQUEST = 8;
private static final int RT_QUERY_REPLY = 9;
private static final int RT_CLOSE_REQUEST = 10;
private static final int RT_CLOSE_REPLY = 11;
private static final int RESP_OK = 0;
private static final int RESP_NOT_OK = 1;
private static final int RESP_FAILED = 2;
private static byte[] transfer_handler_key = new SHA1Simple().calculateHash("Aelitis:NATPuncher:TransferHandlerKey".getBytes());
private boolean started;
private DHTNATPuncherAdapter adapter;
private DHT dht;
private DHTLogger logger;
private PluginInterface plugin_interface;
private Formatters formatters;
private UTTimer timer;
private static final int REPUBLISH_TIME_MIN = 5*60*1000;
private static final int TRANSFER_TIMEOUT = 30*1000;
private static final int RENDEZVOUS_LOOKUP_TIMEOUT = 30*1000;
private static final int TUNNEL_TIMEOUT = 3*1000;
private static final int RENDEZVOUS_SERVER_MAX = 8;
private static final int RENDEZVOUS_SERVER_TIMEOUT = 5*60*1000;
private static final int RENDEZVOUS_CLIENT_PING_PERIOD = 50*1000; // some routers only hold tunnel for 60s
private static final int RENDEZVOUS_PING_FAIL_LIMIT = 4; // if you make this < 2 change code below!
private Monitor server_mon;
private Map rendezvous_bindings = new HashMap();
private long last_publish;
private Monitor pub_mon;
private boolean publish_in_progress;
private volatile DHTTransportContact rendezvous_local_contact;
private volatile DHTTransportContact rendezvous_target;
private volatile DHTTransportContact last_ok_rendezvous;
private static final int FAILED_RENDEZVOUS_HISTORY_MAX = 16;
private Map failed_rendezvous =
new LinkedHashMap(FAILED_RENDEZVOUS_HISTORY_MAX,0.75f,true)
{
protected boolean
removeEldestEntry(
Map.Entry eldest)
{
return size() > FAILED_RENDEZVOUS_HISTORY_MAX;
}
};
private boolean rendezvous_thread_running;
private Map explicit_rendezvous_map = new HashMap();
private Monitor punch_mon;
private List oustanding_punches = new ArrayList();
private DHTTransportContact current_local = null;
private DHTTransportContact current_target = null;
private int rendevzous_fail_count = 0;
public
DHTNATPuncherImpl(
DHTNATPuncherAdapter _adapter,
DHT _dht )
{
adapter = _adapter;
dht = _dht;
logger = dht.getLogger();
plugin_interface = dht.getLogger().getPluginInterface();
formatters = plugin_interface.getUtilities().getFormatters();
pub_mon = plugin_interface.getUtilities().getMonitor();
server_mon = plugin_interface.getUtilities().getMonitor();
punch_mon = plugin_interface.getUtilities().getMonitor();
timer = plugin_interface.getUtilities().createTimer(
"DHTNATPuncher:refresher", true );
}
public void
start()
{
if ( started ){
return;
}
started = true;
DHTTransport transport = dht.getTransport();
transport.addListener(
new DHTTransportListener()
{
public void
localContactChanged(
DHTTransportContact local_contact )
{
publish( false );
}
public void
currentAddress(
String address )
{
}
public void
reachabilityChanged(
boolean reacheable )
{
publish( false );
}
});
transport.registerTransferHandler(
transfer_handler_key,
new DHTTransportTransferHandler()
{
public String
getName()
{
return( "NAT Traversal" );
}
public byte[]
handleRead(
DHTTransportContact originator,
byte[] key )
{
return( null );
}
public byte[]
handleWrite(
DHTTransportContact originator,
byte[] key,
byte[] value )
{
return( receiveRequest((DHTTransportUDPContact)originator, value ));
}
});
timer.addPeriodicEvent(
REPUBLISH_TIME_MIN,
new UTTimerEventPerformer()
{
public void
perform(
UTTimerEvent event )
{
publish( false );
}
});
timer.addPeriodicEvent(
RENDEZVOUS_SERVER_TIMEOUT/2,
new UTTimerEventPerformer()
{
public void
perform(
UTTimerEvent event )
{
long now = plugin_interface.getUtilities().getCurrentSystemTime();
try{
server_mon.enter();
Iterator it = rendezvous_bindings.values().iterator();
while( it.hasNext()){
Object[] entry = (Object[])it.next();
long time = ((Long)entry[1]).longValue();
boolean removed = false;
if ( time > now ){
// clock change, easiest approach is to remove it
it.remove();
removed = true;
}else if ( now - time > RENDEZVOUS_SERVER_TIMEOUT ){
// timeout
it.remove();
removed = true;
}
if ( removed ){
log( "Rendezvous " + ((DHTTransportContact)entry[0]).getString() + " removed due to inactivity" );
}
}
}finally{
server_mon.exit();
}
}
});
publish( false );
}
public boolean
active()
{
return( rendezvous_local_contact != null );
}
public boolean
operational()
{
DHTTransportContact ok = last_ok_rendezvous;
if ( ok != null && ok == rendezvous_target ){
return( true );
}
return( false );
}
protected void
publish(
final boolean force )
{
long now = plugin_interface.getUtilities().getCurrentSystemTime();
if ( now < last_publish && !force ){
last_publish = now;
}else{
if ( force || now - last_publish >= REPUBLISH_TIME_MIN ){
last_publish = now;
plugin_interface.getUtilities().createThread(
"DHTNATPuncher:publisher",
new Runnable()
{
public void
run()
{
try{
pub_mon.enter();
if ( publish_in_progress ){
return;
}
publish_in_progress = true;
}finally{
pub_mon.exit();
}
try{
publishSupport();
}finally{
try{
pub_mon.enter();
publish_in_progress = false;
}finally{
pub_mon.exit();
}
}
}
});
}
}
}
protected void
publishSupport()
{
DHTTransport transport = dht.getTransport();
if ( TESTING || !transport.isReachable() ){
DHTTransportContact local_contact = transport.getLocalContact();
// see if the rendezvous has failed and therefore we are required to find a new one
boolean force =
rendezvous_target != null &&
failed_rendezvous.containsKey( rendezvous_target.getAddress());
if ( rendezvous_local_contact != null && !force ){
if ( local_contact.getAddress().equals( rendezvous_local_contact.getAddress())){
// already running for the current local contact
return;
}
}
DHTTransportContact explicit = (DHTTransportContact)explicit_rendezvous_map.get( local_contact.getAddress());
if ( explicit != null ){
try{
pub_mon.enter();
rendezvous_local_contact = local_contact;
rendezvous_target = explicit;
runRendezvous();
}finally{
pub_mon.exit();
}
}else{
final DHTTransportContact[] new_rendezvous_target = { null };
DHTTransportContact[] reachables = dht.getTransport().getReachableContacts();
int reachables_tried = 0;
int reachables_skipped = 0;
final Semaphore sem = plugin_interface.getUtilities().getSemaphore();
for (int i=0;i<reachables.length;i++){
DHTTransportContact contact = reachables[i];
try{
pub_mon.enter();
// see if we've found a good one yet
if ( new_rendezvous_target[0] != null ){
break;
}
// skip any known bad ones
if ( failed_rendezvous.containsKey( contact.getAddress())){
reachables_skipped++;
sem.release();
continue;
}
}finally{
pub_mon.exit();
}
if ( i > 0 ){
try{
Thread.sleep( 1000 );
}catch( Throwable e ){
}
}
reachables_tried++;
contact.sendPing(
new DHTTransportReplyHandlerAdapter()
{
public void
pingReply(
DHTTransportContact ok_contact )
{
trace( "Punch:" + ok_contact.getString() + " OK" );
try{
pub_mon.enter();
if ( new_rendezvous_target[0] == null ){
new_rendezvous_target[0] = ok_contact;
}
}finally{
pub_mon.exit();
sem.release();
}
}
public void
failed(
DHTTransportContact failed_contact,
Throwable e )
{
try{
trace( "Punch:" + failed_contact.getString() + " Failed" );
}finally{
sem.release();
}
}
});
}
for (int i=0;i<reachables.length;i++){
sem.reserve();
try{
pub_mon.enter();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -