It looks like the NIO network handler works now - some testing needed to see how reliably

This commit is contained in:
2016-08-24 22:17:02 +02:00
parent 3a92bab9ba
commit caa2219a63
6 changed files with 59 additions and 74 deletions

View File

@ -228,10 +228,11 @@ public abstract class AbstractConnection {
break;
case CUSTOM:
MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) payload);
if (response != null) {
if (response == null) {
disconnect();
} else {
send(response);
}
disconnect();
break;
default:
throw new NodeException("Command 'version' or 'verack' expected, but was '"

View File

@ -17,10 +17,7 @@
package ch.dissem.bitmessage.networking.nio;
import ch.dissem.bitmessage.InternalContext;
import ch.dissem.bitmessage.entity.GetData;
import ch.dissem.bitmessage.entity.MessagePayload;
import ch.dissem.bitmessage.entity.NetworkMessage;
import ch.dissem.bitmessage.entity.Version;
import ch.dissem.bitmessage.entity.*;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.exception.NodeException;
@ -112,16 +109,18 @@ public class ConnectionInfo extends AbstractConnection {
public void updateSyncStatus() {
if (!syncFinished) {
syncFinished = reader.getMessages().isEmpty() && syncFinished(null);
syncFinished = (reader == null || reader.getMessages().isEmpty()) && syncFinished(null);
}
}
public boolean isExpired() {
switch (state) {
case CONNECTING:
return lastUpdate < System.currentTimeMillis() - 30000;
// the TCP timeout starts out at 20 seconds
return lastUpdate < System.currentTimeMillis() - 20_000;
case ACTIVE:
return lastUpdate < System.currentTimeMillis() - 30000;
// after verack messages are exchanged, the timeout is raised to 10 minutes
return lastUpdate < System.currentTimeMillis() - 600_000;
case DISCONNECTED:
return true;
default:
@ -150,4 +149,10 @@ public class ConnectionInfo extends AbstractConnection {
commonRequestedObjects.addAll(((GetData) payload).getInventory());
}
}
public boolean isWritePending() {
return !sendingQueue.isEmpty()
|| headerOut != null && headerOut.hasRemaining()
|| payloadOut != null && payloadOut.hasRemaining();
}
}

View File

@ -64,7 +64,7 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
private Selector selector;
private ServerSocketChannel serverChannel;
private Map<ConnectionInfo, SelectionKey> connections = new ConcurrentHashMap<>();
private int requestedObjectsCount;
private volatile int requestedObjectsCount;
private Thread starter;
@ -94,13 +94,15 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
public CustomMessage send(InetAddress server, int port, CustomMessage request) {
try (SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port))) {
channel.configureBlocking(true);
ByteBuffer buffer = ByteBuffer.allocate(MAX_MESSAGE_SIZE);
new NetworkMessage(request).write(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
channel.write(buffer);
ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_SIZE);
ByteBuffer payloadBuffer = new NetworkMessage(request).writeHeaderAndGetPayloadBuffer(headerBuffer);
headerBuffer.flip();
while (headerBuffer.hasRemaining()) {
channel.write(headerBuffer);
}
while (payloadBuffer.hasRemaining()) {
channel.write(payloadBuffer);
}
buffer.clear();
V3MessageReader reader = new V3MessageReader();
while (channel.isConnected() && reader.getMessages().isEmpty()) {
@ -195,11 +197,25 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
if (missing > 0) {
List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(missing, ctx.getStreams());
for (NetworkAddress address : addresses) {
if (isConnectedTo(address)) {
continue;
}
try {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(address.toInetAddress(), address.getPort()));
channel.finishConnect();
long timeout = System.currentTimeMillis() + 20_000;
while (!channel.finishConnect() && System.currentTimeMillis() < timeout) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
}
if (!channel.finishConnect()) {
channel.close();
continue;
}
ConnectionInfo connection = new ConnectionInfo(ctx, CLIENT,
address,
listener,
@ -248,6 +264,7 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (key.attachment() instanceof ConnectionInfo) {
SocketChannel channel = (SocketChannel) key.channel();
ConnectionInfo connection = (ConnectionInfo) key.attachment();
@ -258,24 +275,18 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
if (key.isReadable()) {
read(channel, connection);
}
if (connection.getSendingQueue().isEmpty()) {
if (connection.getState() == DISCONNECTED) {
key.interestOps(0);
key.channel().close();
} else {
key.interestOps(OP_READ);
}
} else {
if (connection.getState() == DISCONNECTED) {
key.interestOps(0);
channel.close();
} else if (connection.isWritePending()) {
key.interestOps(OP_READ | OP_WRITE);
} else {
key.interestOps(OP_READ);
}
} catch (CancelledKeyException | NodeException | IOException e) {
connection.disconnect();
}
if (connection.getState() == DISCONNECTED) {
connections.remove(connection);
}
}
keyIterator.remove();
requestedObjectsCount = requestedObjects.size();
}
for (Map.Entry<ConnectionInfo, SelectionKey> e : connections.entrySet()) {
@ -316,7 +327,7 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
}
private static void read(SocketChannel channel, ConnectionInfo connection) throws IOException {
while (channel.read(connection.getInBuffer()) > 0) {
if (channel.read(connection.getInBuffer()) > 0) {
connection.updateReader();
}
connection.updateSyncStatus();
@ -442,6 +453,15 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
);
}
private boolean isConnectedTo(NetworkAddress address) {
for (ConnectionInfo c : connections.keySet()) {
if (c.getNode().equals(address)) {
return true;
}
}
return false;
}
@Override
public boolean isRunning() {
return selector != null && selector.isOpen() && starter.isAlive();