/** * check to see if we have lost connection with the remote node and if we have attempts a * reconnect. * * @param key the key relating to the heartbeat that we are checking * @param approxTimeOutTime the approximate time in milliseconds * @throws ConnectException */ private void heartbeatCheckHasReceived( @NotNull final SelectionKey key, final long approxTimeOutTime) throws ConnectException { final Attached attached = (Attached) key.attachment(); // we wont attempt to reconnect the server socket if (attached.isServer || !attached.isHandShakingComplete()) return; final SocketChannel channel = (SocketChannel) key.channel(); if (approxTimeOutTime > attached.entryReader.lastHeartBeatReceived + attached.remoteHeartbeatInterval) { if (LOG.isDebugEnabled()) LOG.debug( "lost connection, attempting to reconnect. " + "missed heartbeat from identifier=" + attached.remoteIdentifier); try { channel.socket().close(); channel.close(); activeKeys.clear(attached.remoteIdentifier); closeables.remove(channel); } catch (IOException e) { LOG.debug("", e); } attached.connector.connectLater(); } }
/** * reads from the socket and writes them to the buffer * * @param socketChannel the socketChannel to read from * @return the number of bytes read * @throws IOException */ private int readSocketToBuffer(@NotNull final SocketChannel socketChannel) throws IOException { compactBuffer(); final int len = socketChannel.read(in); out.limit(in.position()); return len; }
/** * writes the contents of the buffer to the socket * * @param socketChannel the socket to publish the buffer to * @param approxTime an approximation of the current time in millis * @throws IOException */ private int writeBufferToSocket( @NotNull final SocketChannel socketChannel, final long approxTime) throws IOException { if (in.position() == 0) return 0; // if we still have some unwritten writer from last time lastSentTime = approxTime; out.limit((int) in.position()); final int len = socketChannel.write(out); if (LOG.isDebugEnabled()) LOG.debug("bytes-written=" + len); if (out.remaining() == 0) { out.clear(); in.clear(); } else { out.compact(); in.position(out.position()); in.limit(in.capacity()); out.clear(); } return len; }
/** blocks until connected */ @Override SelectableChannel doConnect() throws IOException, InterruptedException { boolean success = false; final SocketChannel socketChannel = SocketChannel.open(); try { socketChannel.configureBlocking(false); socketChannel.socket().setReuseAddress(true); socketChannel.socket().setSoLinger(false, 0); socketChannel.socket().setSoTimeout(0); socketChannel.socket().setTcpNoDelay(true); try { socketChannel.connect(details.address()); } catch (UnresolvedAddressException e) { this.connectLater(); } // Under experiment, the concoction was found to be more successful if we // paused before registering the OP_CONNECT Thread.sleep(10); // the registration has be be run on the same thread as the selector addPendingRegistration( new Runnable() { @Override public void run() { final Attached attached = new Attached(); attached.connector = ClientConnector.this; try { socketChannel.register(selector, OP_CONNECT, attached); } catch (ClosedChannelException e) { if (socketChannel.isOpen()) LOG.error("", e); } } }); selector.wakeup(); success = true; return socketChannel; } finally { if (!success) { try { try { socketChannel.socket().close(); } catch (Exception e) { LOG.error("", e); } socketChannel.close(); } catch (IOException e) { LOG.error("", e); } } } }
/** called when the selector receives a OP_ACCEPT message */ private void onAccept(@NotNull final SelectionKey key) throws IOException { final ServerSocketChannel server = (ServerSocketChannel) key.channel(); final SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.socket().setReuseAddress(true); channel.socket().setTcpNoDelay(true); channel.socket().setSoTimeout(0); channel.socket().setSoLinger(false, 0); final Attached attached = new Attached(); channel.register(selector, OP_WRITE | OP_READ, attached); throttle(channel); attached.entryReader = new TcpSocketChannelEntryReader(); attached.entryWriter = new TcpSocketChannelEntryWriter(); attached.isServer = true; attached.entryWriter.identifierToBuffer(localIdentifier); }
/** * removes back in the OP_WRITE from the selector, otherwise it'll spin loop. The OP_WRITE will * get added back in as soon as we have data to write * * @param socketChannel the socketChannel we wish to stop writing to * @param attached data associated with the socketChannels key */ public synchronized void disableWrite( @NotNull final SocketChannel socketChannel, @NotNull final Attached attached) { try { SelectionKey key = socketChannel.keyFor(selector); if (key != null) { if (attached.isHandShakingComplete() && selector.isOpen()) { if (LOG.isDebugEnabled()) LOG.debug( "Disabling OP_WRITE to remoteIdentifier=" + attached.remoteIdentifier + ", localIdentifier=" + localIdentifier); key.interestOps(key.interestOps() & ~OP_WRITE); } } } catch (Exception e) { LOG.error("", e); } }
/** called when the selector receives a OP_CONNECT message */ private void onConnect(@NotNull final SelectionKey key) throws IOException, InterruptedException { final SocketChannel channel = (SocketChannel) key.channel(); final Attached attached = (Attached) key.attachment(); try { if (!channel.finishConnect()) { return; } } catch (SocketException e) { quietClose(key, e); attached.connector.connect(); throw e; } attached.connector.setSuccessfullyConnected(); if (LOG.isDebugEnabled()) LOG.debug( "successfully connected to {}, local-id={}", channel.socket().getInetAddress(), localIdentifier); channel.configureBlocking(false); channel.socket().setTcpNoDelay(true); channel.socket().setSoTimeout(0); channel.socket().setSoLinger(false, 0); attached.entryReader = new TcpSocketChannelEntryReader(); attached.entryWriter = new TcpSocketChannelEntryWriter(); key.interestOps(OP_WRITE | OP_READ); throttle(channel); // register it with the selector and store the ModificationIterator for this key attached.entryWriter.identifierToBuffer(localIdentifier); }