mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 06:44:16 +00:00
ClientConnectionManager.openConnection now returns a future. Some logging about connection failures was removed.
This commit is contained in:
parent
200dc1294c
commit
ae585608e6
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.bitcoinj.net;
|
package org.bitcoinj.net;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -47,6 +49,7 @@ public class BlockingClient implements MessageWriteTarget {
|
|||||||
private final ByteBuffer dbuf;
|
private final ByteBuffer dbuf;
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private volatile boolean vCloseRequested = false;
|
private volatile boolean vCloseRequested = false;
|
||||||
|
private SettableFuture<SocketAddress> connectFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a new client to the given server address using the given {@link StreamParser} to decode the data.
|
* <p>Creates a new client to the given server address using the given {@link StreamParser} to decode the data.
|
||||||
@ -62,6 +65,7 @@ public class BlockingClient implements MessageWriteTarget {
|
|||||||
*/
|
*/
|
||||||
public BlockingClient(final SocketAddress serverAddress, final StreamParser parser,
|
public BlockingClient(final SocketAddress serverAddress, final StreamParser parser,
|
||||||
final int connectTimeoutMillis, final SocketFactory socketFactory, @Nullable final Set<BlockingClient> clientSet) throws IOException {
|
final int connectTimeoutMillis, final SocketFactory socketFactory, @Nullable final Set<BlockingClient> clientSet) throws IOException {
|
||||||
|
connectFuture = SettableFuture.create();
|
||||||
// Try to fit at least one message in the network buffer, but place an upper and lower limit on its size to make
|
// Try to fit at least one message in the network buffer, but place an upper and lower limit on its size to make
|
||||||
// sure it doesnt get too large or have to call read too often.
|
// sure it doesnt get too large or have to call read too often.
|
||||||
dbuf = ByteBuffer.allocateDirect(Math.min(Math.max(parser.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND));
|
dbuf = ByteBuffer.allocateDirect(Math.min(Math.max(parser.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND));
|
||||||
@ -73,9 +77,9 @@ public class BlockingClient implements MessageWriteTarget {
|
|||||||
if (clientSet != null)
|
if (clientSet != null)
|
||||||
clientSet.add(BlockingClient.this);
|
clientSet.add(BlockingClient.this);
|
||||||
try {
|
try {
|
||||||
InetSocketAddress iServerAddress = (InetSocketAddress)serverAddress;
|
|
||||||
socket.connect(serverAddress, connectTimeoutMillis);
|
socket.connect(serverAddress, connectTimeoutMillis);
|
||||||
parser.connectionOpened();
|
parser.connectionOpened();
|
||||||
|
connectFuture.set(serverAddress);
|
||||||
InputStream stream = socket.getInputStream();
|
InputStream stream = socket.getInputStream();
|
||||||
byte[] readBuff = new byte[dbuf.capacity()];
|
byte[] readBuff = new byte[dbuf.capacity()];
|
||||||
|
|
||||||
@ -97,8 +101,10 @@ public class BlockingClient implements MessageWriteTarget {
|
|||||||
dbuf.compact();
|
dbuf.compact();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (!vCloseRequested)
|
if (!vCloseRequested) {
|
||||||
log.error("Error trying to open/read from connection: " + serverAddress, e);
|
log.error("Error trying to open/read from connection: " + serverAddress, e);
|
||||||
|
connectFuture.setException(e);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
@ -143,4 +149,9 @@ public class BlockingClient implements MessageWriteTarget {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a future that completes once connection has occurred at the socket level or with an exception if failed to connect. */
|
||||||
|
public ListenableFuture<SocketAddress> getConnectFuture() {
|
||||||
|
return connectFuture;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package org.bitcoinj.net;
|
package org.bitcoinj.net;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.AbstractIdleService;
|
import com.google.common.util.concurrent.AbstractIdleService;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -54,11 +55,11 @@ public class BlockingClientManager extends AbstractIdleService implements Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openConnection(SocketAddress serverAddress, StreamParser parser) {
|
public ListenableFuture<SocketAddress> openConnection(SocketAddress serverAddress, StreamParser parser) {
|
||||||
if (!isRunning())
|
|
||||||
throw new IllegalStateException();
|
|
||||||
try {
|
try {
|
||||||
new BlockingClient(serverAddress, parser, connectTimeoutMillis, socketFactory, clients);
|
if (!isRunning())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
return new BlockingClient(serverAddress, parser, connectTimeoutMillis, socketFactory, clients).getConnectFuture();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e); // This should only happen if we are, eg, out of system resources
|
throw new RuntimeException(e); // This should only happen if we are, eg, out of system resources
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.bitcoinj.net;
|
package org.bitcoinj.net;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.Service;
|
import com.google.common.util.concurrent.Service;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@ -31,7 +32,7 @@ public interface ClientConnectionManager extends Service {
|
|||||||
/**
|
/**
|
||||||
* Creates a new connection to the given address, with the given parser used to handle incoming data.
|
* Creates a new connection to the given address, with the given parser used to handle incoming data.
|
||||||
*/
|
*/
|
||||||
void openConnection(SocketAddress serverAddress, StreamParser parser);
|
ListenableFuture<SocketAddress> openConnection(SocketAddress serverAddress, StreamParser parser);
|
||||||
|
|
||||||
/** Gets the number of connected peers */
|
/** Gets the number of connected peers */
|
||||||
int getConnectedClientCount();
|
int getConnectedClientCount();
|
||||||
|
@ -18,9 +18,13 @@ package org.bitcoinj.net;
|
|||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.util.concurrent.AbstractExecutionThreadService;
|
import com.google.common.util.concurrent.AbstractExecutionThreadService;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.ConnectException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.nio.channels.spi.SelectorProvider;
|
import java.nio.channels.spi.SelectorProvider;
|
||||||
@ -36,12 +40,15 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
|
|
||||||
private final Selector selector;
|
private final Selector selector;
|
||||||
|
|
||||||
// SocketChannels and StreamParsers of newly-created connections which should be registered with OP_CONNECT
|
class PendingConnect {
|
||||||
class SocketChannelAndParser {
|
SocketChannel sc;
|
||||||
SocketChannel sc; StreamParser parser;
|
StreamParser parser;
|
||||||
SocketChannelAndParser(SocketChannel sc, StreamParser parser) { this.sc = sc; this.parser = parser; }
|
SocketAddress address;
|
||||||
|
SettableFuture<SocketAddress> future = SettableFuture.create();
|
||||||
|
|
||||||
|
PendingConnect(SocketChannel sc, StreamParser parser, SocketAddress address) { this.sc = sc; this.parser = parser; this.address = address; }
|
||||||
}
|
}
|
||||||
final Queue<SocketChannelAndParser> newConnectionChannels = new LinkedBlockingQueue<SocketChannelAndParser>();
|
final Queue<PendingConnect> newConnectionChannels = new LinkedBlockingQueue<PendingConnect>();
|
||||||
|
|
||||||
// Added to/removed from by the individual ConnectionHandler's, thus must by synchronized on its own.
|
// Added to/removed from by the individual ConnectionHandler's, thus must by synchronized on its own.
|
||||||
private final Set<ConnectionHandler> connectedHandlers = Collections.synchronizedSet(new HashSet<ConnectionHandler>());
|
private final Set<ConnectionHandler> connectedHandlers = Collections.synchronizedSet(new HashSet<ConnectionHandler>());
|
||||||
@ -51,17 +58,21 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
// We could have a !isValid() key here if the connection is already closed at this point
|
// We could have a !isValid() key here if the connection is already closed at this point
|
||||||
if (key.isValid() && key.isConnectable()) { // ie a client connection which has finished the initial connect process
|
if (key.isValid() && key.isConnectable()) { // ie a client connection which has finished the initial connect process
|
||||||
// Create a ConnectionHandler and hook everything together
|
// Create a ConnectionHandler and hook everything together
|
||||||
StreamParser parser = (StreamParser) key.attachment();
|
PendingConnect data = (PendingConnect) key.attachment();
|
||||||
|
StreamParser parser = data.parser;
|
||||||
SocketChannel sc = (SocketChannel) key.channel();
|
SocketChannel sc = (SocketChannel) key.channel();
|
||||||
ConnectionHandler handler = new ConnectionHandler(parser, key, connectedHandlers);
|
ConnectionHandler handler = new ConnectionHandler(parser, key, connectedHandlers);
|
||||||
try {
|
try {
|
||||||
if (sc.finishConnect()) {
|
if (sc.finishConnect()) {
|
||||||
log.info("Successfully connected to {}", sc.socket().getRemoteSocketAddress());
|
log.info("Successfully connected to {}", sc.socket().getRemoteSocketAddress());
|
||||||
key.interestOps((key.interestOps() | SelectionKey.OP_READ) & ~SelectionKey.OP_CONNECT).attach(handler);
|
key.interestOps((key.interestOps() | SelectionKey.OP_READ) & ~SelectionKey.OP_CONNECT).attach(handler);
|
||||||
handler.parser.connectionOpened();
|
parser.connectionOpened();
|
||||||
|
data.future.set(data.address);
|
||||||
} else {
|
} else {
|
||||||
log.error("Failed to connect to {}", sc.socket().getRemoteSocketAddress());
|
log.error("Failed to connect to {}", sc.socket().getRemoteSocketAddress());
|
||||||
handler.closeConnection(); // Failed to connect for some reason
|
handler.closeConnection(); // Failed to connect for some reason
|
||||||
|
data.future.setException(new ConnectException("Unknown reason"));
|
||||||
|
data.future = null;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If e is a CancelledKeyException, there is a race to get to interestOps after finishConnect() which
|
// If e is a CancelledKeyException, there is a race to get to interestOps after finishConnect() which
|
||||||
@ -70,6 +81,8 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
Throwable cause = Throwables.getRootCause(e);
|
Throwable cause = Throwables.getRootCause(e);
|
||||||
log.error("Failed to connect with exception: {}: {}", cause.getClass().getName(), cause.getMessage());
|
log.error("Failed to connect with exception: {}: {}", cause.getClass().getName(), cause.getMessage());
|
||||||
handler.closeConnection();
|
handler.closeConnection();
|
||||||
|
data.future.setException(cause);
|
||||||
|
data.future = null;
|
||||||
}
|
}
|
||||||
} else // Process bytes read
|
} else // Process bytes read
|
||||||
ConnectionHandler.handleKey(key);
|
ConnectionHandler.handleKey(key);
|
||||||
@ -92,11 +105,11 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
try {
|
try {
|
||||||
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
|
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
|
||||||
while (isRunning()) {
|
while (isRunning()) {
|
||||||
SocketChannelAndParser conn;
|
PendingConnect conn;
|
||||||
while ((conn = newConnectionChannels.poll()) != null) {
|
while ((conn = newConnectionChannels.poll()) != null) {
|
||||||
try {
|
try {
|
||||||
SelectionKey key = conn.sc.register(selector, SelectionKey.OP_CONNECT);
|
SelectionKey key = conn.sc.register(selector, SelectionKey.OP_CONNECT);
|
||||||
key.attach(conn.parser);
|
key.attach(conn);
|
||||||
} catch (ClosedChannelException e) {
|
} catch (ClosedChannelException e) {
|
||||||
log.info("SocketChannel was closed before it could be registered");
|
log.info("SocketChannel was closed before it could be registered");
|
||||||
}
|
}
|
||||||
@ -134,7 +147,7 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openConnection(SocketAddress serverAddress, StreamParser parser) {
|
public ListenableFuture<SocketAddress> openConnection(SocketAddress serverAddress, StreamParser parser) {
|
||||||
if (!isRunning())
|
if (!isRunning())
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
// Create a new connection, give it a parser as an attachment
|
// Create a new connection, give it a parser as an attachment
|
||||||
@ -142,14 +155,12 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
|||||||
SocketChannel sc = SocketChannel.open();
|
SocketChannel sc = SocketChannel.open();
|
||||||
sc.configureBlocking(false);
|
sc.configureBlocking(false);
|
||||||
sc.connect(serverAddress);
|
sc.connect(serverAddress);
|
||||||
newConnectionChannels.offer(new SocketChannelAndParser(sc, parser));
|
PendingConnect data = new PendingConnect(sc, parser, serverAddress);
|
||||||
|
newConnectionChannels.offer(data);
|
||||||
selector.wakeup();
|
selector.wakeup();
|
||||||
} catch (IOException e) {
|
return data.future;
|
||||||
log.error("Could not connect to " + serverAddress);
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException(e); // This should only happen if we are, eg, out of system resources
|
return Futures.immediateFailedFuture(e);
|
||||||
} catch (AssertionError e) {
|
|
||||||
log.error("Could not connect to " + serverAddress);
|
|
||||||
throw new RuntimeException(e); // Happens on Android when libcore.io.Posix.getsockname() throws libcore.io.ErrnoException.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user