Change NetworkConnection API to separate connect method.

This allows the Peer to close the connection earlier when connect()
takes a long time.

Resolves issue 161.
This commit is contained in:
Miron Cuperman 2012-03-26 15:23:14 -07:00
parent 92398d2c47
commit 9474eaa0d4
6 changed files with 58 additions and 30 deletions

View File

@ -31,6 +31,15 @@ import java.io.IOException;
* Construction is blocking whilst the protocol version is negotiated.
*/
public interface NetworkConnection {
/**
* Connect to the remote peer.
*
* @param peerAddress the address of the remote peer
* @param connectTimeoutMsec timeout in milliseconds
*/
public void connect(PeerAddress peerAddress, int connectTimeoutMsec)
throws IOException, ProtocolException;
/**
* Sends a "ping" message to the remote node. The protocol doesn't presently use this feature much.
*

View File

@ -154,7 +154,8 @@ public class Peer {
*/
public synchronized void connect() throws PeerException {
try {
conn = new TCPNetworkConnection(address, params, CONNECT_TIMEOUT_MSEC, false, versionMessage);
conn = new TCPNetworkConnection(params, false, versionMessage);
conn.connect(address, CONNECT_TIMEOUT_MSEC);
} catch (IOException ex) {
throw new PeerException(ex);
} catch (ProtocolException ex) {

View File

@ -40,16 +40,18 @@ public class TCPNetworkConnection implements NetworkConnection {
private static final Logger log = LoggerFactory.getLogger(TCPNetworkConnection.class);
private final Socket socket;
private final OutputStream out;
private final InputStream in;
private OutputStream out;
private InputStream in;
// The IP address to which we are connecting.
private final InetAddress remoteIp;
private InetAddress remoteIp;
private final NetworkParameters params;
private final VersionMessage versionMessage;
private VersionMessage versionMessage;
// Given to the BitcoinSerializer to de-duplicate messages.
private static final LinkedHashMap<Sha256Hash, Integer> dedupeList = BitcoinSerializer.createDedupeList();
private BitcoinSerializer serializer = null;
private VersionMessage myVersionMessage;
private static final Date checksummingProtocolChangeDate = new Date(1329696000000L);
/**
@ -65,30 +67,35 @@ public class TCPNetworkConnection implements NetworkConnection {
* @throws IOException if there is a network related failure.
* @throws ProtocolException if the version negotiation failed.
*/
public TCPNetworkConnection(PeerAddress peerAddress, NetworkParameters params,
int connectTimeoutMsec, boolean dedupe, VersionMessage ver)
public TCPNetworkConnection(NetworkParameters params, boolean dedupe, VersionMessage ver)
throws IOException, ProtocolException {
this.params = params;
this.remoteIp = peerAddress.getAddr();
this.myVersionMessage = ver;
int port = (peerAddress.getPort() > 0) ? peerAddress.getPort() : params.port;
socket = new Socket();
// So pre-Feb 2012, update checkumming property after version is read.
this.serializer = new BitcoinSerializer(this.params, false, dedupe ? dedupeList : null);
this.serializer.setUseChecksumming(Utils.now().after(checksummingProtocolChangeDate));
}
public void connect(PeerAddress peerAddress, int connectTimeoutMsec)
throws IOException, ProtocolException {
remoteIp = peerAddress.getAddr();
int port = (peerAddress.getPort() > 0) ? peerAddress.getPort() : this.params.port;
InetSocketAddress address = new InetSocketAddress(remoteIp, port);
socket = new Socket();
socket.connect(address, connectTimeoutMsec);
out = socket.getOutputStream();
in = socket.getInputStream();
// The version message does not use checksumming, until Feb 2012 when it magically does.
// So pre-Feb 2012, update checkumming property after version is read.
this.serializer = new BitcoinSerializer(params, false, dedupe ? dedupeList : null);
this.serializer.setUseChecksumming(Utils.now().after(checksummingProtocolChangeDate));
// Announce ourselves. This has to come first to connect to clients beyond v0.30.20.2 which wait to hear
// from us until they send their version message back.
log.info("Announcing ourselves as: {}", ver.subVer);
writeMessage(ver);
log.info("Announcing ourselves as: {}", myVersionMessage.subVer);
writeMessage(myVersionMessage);
// When connecting, the remote peer sends us a version message with various bits of
// useful data in it. We need to know the peer protocol version before we can talk to it.
Message m = readMessage();
@ -140,15 +147,15 @@ public class TCPNetworkConnection implements NetworkConnection {
* @throws IOException if there is a network related failure.
* @throws ProtocolException if the version negotiation failed.
*/
public TCPNetworkConnection(PeerAddress peerAddress, NetworkParameters params,
int bestHeight, int connectTimeoutMsec, boolean dedupe)
public TCPNetworkConnection(NetworkParameters params,
int bestHeight, boolean dedupe)
throws IOException, ProtocolException {
this(peerAddress, params, connectTimeoutMsec, dedupe, new VersionMessage(params, bestHeight));
this(params, dedupe, new VersionMessage(params, bestHeight));
}
public TCPNetworkConnection(InetAddress inetAddress, NetworkParameters params, int bestHeight, int connectTimeout)
public TCPNetworkConnection(NetworkParameters params, int bestHeight)
throws IOException, ProtocolException {
this(new PeerAddress(inetAddress), params, bestHeight, connectTimeout, true);
this(params, bestHeight, true);
}

View File

@ -37,13 +37,13 @@ public class MockNetworkConnection implements NetworkConnection {
private PeerAddress peerAddress;
public MockNetworkConnection() {
}
public void connect(PeerAddress peerAddress, int connectTimeoutMsec) {
inboundMessageQ = new ArrayBlockingQueue<Object>(10);
outboundMessageQ = new ArrayBlockingQueue<Message>(10);
try {
peerAddress = new PeerAddress(InetAddress.getLocalHost(), fakePort++);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Cannot happen.
}
this.peerAddress = peerAddress;
}
public void ping() throws IOException {

View File

@ -21,6 +21,8 @@ import com.google.bitcoin.utils.BriefLogFormatter;
import org.easymock.IMocksControl;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import static org.easymock.EasyMock.createStrictControl;
@ -35,6 +37,7 @@ public class TestWithNetworkConnections {
protected Wallet wallet;
protected ECKey key;
protected Address address;
private static int fakePort;
public void setUp() throws Exception {
BriefLogFormatter.init();
@ -52,7 +55,13 @@ public class TestWithNetworkConnections {
}
protected MockNetworkConnection createMockNetworkConnection() {
return new MockNetworkConnection();
MockNetworkConnection conn = new MockNetworkConnection();
try {
conn.connect(new PeerAddress(InetAddress.getLocalHost(), fakePort++), 0);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Cannot happen
}
return conn;
}
protected void runPeer(Peer peer, MockNetworkConnection connection) throws IOException, PeerException {
@ -65,7 +74,7 @@ public class TestWithNetworkConnections {
}
}
protected void runPeerAsync(final Peer peer, MockNetworkConnection connection) throws IOException, PeerException {
protected void runPeerAsync(final Peer peer, MockNetworkConnection connection) {
new Thread("Test Peer Thread") {
@Override
public void run() {

View File

@ -18,6 +18,7 @@ package com.google.bitcoin.examples;
import com.google.bitcoin.core.NetworkConnection;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.PeerAddress;
import com.google.bitcoin.core.TCPNetworkConnection;
import com.google.bitcoin.discovery.DnsDiscovery;
import com.google.bitcoin.discovery.IrcDiscovery;
@ -94,8 +95,9 @@ public class PrintPeers {
pool.submit(new Runnable() {
public void run() {
try {
NetworkConnection conn = new TCPNetworkConnection(addr,
NetworkParameters.prodNet(), 0, 1000);
NetworkConnection conn =
new TCPNetworkConnection(NetworkParameters.prodNet(), 0);
conn.connect(new PeerAddress(addr), 1000);
synchronized (lock) {
long nodeHeight = conn.getVersionMessage().bestHeight;
long diff = bestHeight[0] - nodeHeight;