import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.ref.Reference;
+import java.net.*;
import java.util.LinkedList;
-import java.net.SocketAddress;
-import java.net.StandardSocketOptions;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.*;
(peer.key.interestOps() | SelectionKey.OP_READ) & (~SelectionKey.OP_WRITE)));
}
return written;
- } catch (IOException e) {
+ } catch (IOException|CancelledKeyException ignored) {
// Most likely the socket is disconnected, let the background thread handle it.
return 0;
}
public void disconnect_socket() {
try {
do_selector_action(() -> {
- peer.key.cancel();
+ try { peer.key.cancel(); } catch (CancelledKeyException ignored) {}
peer.key.channel().close();
});
} catch (IOException ignored) { }
long socket_id;
volatile boolean shutdown = false;
+ private static Option_SocketAddressZ get_netaddr_from_sockaddr(java.net.SocketAddress sockaddr) {
+ if (sockaddr instanceof InetSocketAddress) {
+ InetAddress addr = ((InetSocketAddress) sockaddr).getAddress();
+ short port = (short) ((InetSocketAddress) sockaddr).getPort();
+ if (addr instanceof Inet4Address) {
+ return Option_SocketAddressZ.some(org.ldk.structs.SocketAddress.tcp_ip_v4(addr.getAddress(), port));
+ } else if (addr instanceof Inet6Address) {
+ return Option_SocketAddressZ.some(org.ldk.structs.SocketAddress.tcp_ip_v6(addr.getAddress(), port));
+ }
+ }
+ return Option_SocketAddressZ.none();
+ }
+
/**
* Constructs a new peer handler, spawning a thread to monitor for socket events.
*
try {
Peer peer = setup_socket(chan);
peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer);
- Result_NonePeerHandleErrorZ res = this.peer_manager.new_inbound_connection(peer.descriptor);
+ Option_SocketAddressZ netaddr = get_netaddr_from_sockaddr(chan.getRemoteAddress());
+ Result_NonePeerHandleErrorZ res = this.peer_manager.new_inbound_connection(peer.descriptor, netaddr);
if (res instanceof Result_NonePeerHandleErrorZ.Result_NonePeerHandleErrorZ_Err) {
peer.descriptor.disconnect_socket();
}
if (key.isValid() && (key.interestOps() & SelectionKey.OP_WRITE) != 0 && key.isWritable()) {
Result_NonePeerHandleErrorZ res = this.peer_manager.write_buffer_space_avail(peer.descriptor);
if (res instanceof Result_NonePeerHandleErrorZ.Result_NonePeerHandleErrorZ_Err) {
- key.channel().close();
key.cancel();
+ key.channel().close();
}
}
if (key.isValid() && (key.interestOps() & SelectionKey.OP_READ) != 0 && key.isReadable()) {
if (read == -1) {
this.peer_manager.socket_disconnected(peer.descriptor);
key.cancel();
+ key.channel().close(); // This may throw, we read -1 so the channel should already be closed, but do this to be safe
} else if (read > 0) {
((Buffer)buf).flip();
// This code is quite hot during initial network graph sync, so we go a ways out of
key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
}
} else {
- key.channel().close();
key.cancel();
+ key.channel().close();
}
bindings.CResult_boolPeerHandleErrorZ_free(read_result_pointer);
}
}
} catch (IOException ignored) {
- try { key.channel().close(); } catch (IOException ignored2) { }
key.cancel();
+ try { key.channel().close(); } catch (IOException ignored2) { }
peer_manager.socket_disconnected(peer.descriptor);
}
} catch (CancelledKeyException e) {
* @param timeout_ms The amount of time, in milliseconds, up to which we will wait for connection to complete.
* @throws IOException If connecting to the remote endpoint fails or internal java.nio errors occur.
*/
- public void connect(byte[] their_node_id, SocketAddress remote, int timeout_ms) throws IOException {
+ public void connect(byte[] their_node_id, java.net.SocketAddress remote, int timeout_ms) throws IOException {
SocketChannel chan = SocketChannel.open();
- chan.configureBlocking(false);
- Selector open_selector = Selector.open();
- chan.register(open_selector, SelectionKey.OP_CONNECT);
- if (!chan.connect(remote)) {
- open_selector.select(timeout_ms);
+ boolean connected;
+ try {
+ chan.configureBlocking(false);
+ Selector open_selector = Selector.open();
+ chan.register(open_selector, SelectionKey.OP_CONNECT);
+ if (!chan.connect(remote)) {
+ open_selector.select(timeout_ms);
+ }
+ connected = chan.finishConnect();
+ } catch (IOException e) {
+ try { chan.close(); } catch (IOException _e) { }
+ throw e;
}
- if (!chan.finishConnect()) { // Note that this may throw its own IOException if we failed for another reason
+ if (!connected) {
+ try { chan.close(); } catch (IOException _e) { }
throw new IOException("Timed out");
}
Peer peer = setup_socket(chan);
do_selector_action(() -> peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer));
- Result_CVec_u8ZPeerHandleErrorZ res = this.peer_manager.new_outbound_connection(their_node_id, peer.descriptor);
+ Result_CVec_u8ZPeerHandleErrorZ res = this.peer_manager.new_outbound_connection(their_node_id, peer.descriptor, get_netaddr_from_sockaddr(remote));
if (res instanceof Result_CVec_u8ZPeerHandleErrorZ.Result_CVec_u8ZPeerHandleErrorZ_OK) {
byte[] initial_bytes = ((Result_CVec_u8ZPeerHandleErrorZ.Result_CVec_u8ZPeerHandleErrorZ_OK) res).res;
if (chan.write(ByteBuffer.wrap(initial_bytes)) != initial_bytes.length) {
* @param their_node_id must be a valid 33-byte public key
*/
public void disconnect(byte[] their_node_id) {
- this.peer_manager.disconnect_by_node_id(their_node_id, false);
+ this.peer_manager.disconnect_by_node_id(their_node_id);
}
/**
* @param socket_address The address to bind the listening socket to.
* @throws IOException if binding the listening socket fail.
*/
- public void bind_listener(SocketAddress socket_address) throws IOException {
+ public void bind_listener(java.net.SocketAddress socket_address) throws IOException {
ServerSocketChannel listen_channel = ServerSocketChannel.open();
listen_channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
listen_channel.bind(socket_address);
}
/**
- * Interrupt the background thread, stopping all peer handling. Disconnection events to the PeerHandler are not made,
- * potentially leaving the PeerHandler in an inconsistent state.
+ * Interrupt the background thread, stopping all peer handling.
+ *
+ * After this method is called, the behavior of future calls to methods on this NioPeerHandler are undefined.
*/
public void interrupt() {
+ this.peer_manager.disconnect_all_peers();
shutdown = true;
selector.wakeup();
try {
for (ServerSocketChannel chan : listening_sockets) {
chan.close();
}
+ listening_sockets.clear();
} catch (IOException ignored) {}
}
Reference.reachabilityFence(this.peer_manager); // Almost certainly overkill, but no harm in it