com.couchbase.client.core.deps.org.xbill.DNS.TCPClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-io Show documentation
Show all versions of core-io Show documentation
The official Couchbase JVM Core IO Library
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2005 Brian Wellington ([email protected])
package com.couchbase.client.core.deps.org.xbill.DNS;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
final class TCPClient {
private final long startTime;
private final Duration timeout;
private final SelectionKey key;
TCPClient(Duration timeout) throws IOException {
this.timeout = timeout;
startTime = System.nanoTime();
boolean done = false;
Selector selector = null;
SocketChannel channel = SocketChannel.open();
try {
selector = Selector.open();
channel.configureBlocking(false);
key = channel.register(selector, SelectionKey.OP_READ);
done = true;
} finally {
if (!done && selector != null) {
selector.close();
}
if (!done) {
channel.close();
}
}
}
void bind(SocketAddress addr) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
channel.socket().bind(addr);
}
void connect(SocketAddress addr) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.connect(addr)) {
return;
}
key.interestOps(SelectionKey.OP_CONNECT);
try {
while (!channel.finishConnect()) {
if (!key.isConnectable()) {
blockUntil(key);
}
}
} finally {
if (key.isValid()) {
key.interestOps(0);
}
}
}
void send(byte[] data) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
NioClient.verboseLog(
"TCP write",
channel.socket().getLocalSocketAddress(),
channel.socket().getRemoteSocketAddress(),
data);
byte[] lengthArray = new byte[2];
lengthArray[0] = (byte) (data.length >>> 8);
lengthArray[1] = (byte) (data.length & 0xFF);
ByteBuffer[] buffers = new ByteBuffer[2];
buffers[0] = ByteBuffer.wrap(lengthArray);
buffers[1] = ByteBuffer.wrap(data);
int nsent = 0;
key.interestOps(SelectionKey.OP_WRITE);
try {
while (nsent < data.length + 2) {
if (key.isWritable()) {
long n = channel.write(buffers);
if (n < 0) {
throw new EOFException();
}
nsent += (int) n;
if (nsent < data.length + 2 && System.nanoTime() - startTime >= timeout.toNanos()) {
throw new SocketTimeoutException();
}
} else {
blockUntil(key);
}
}
} finally {
if (key.isValid()) {
key.interestOps(0);
}
}
}
private byte[] _recv(int length) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
int nrecvd = 0;
byte[] data = new byte[length];
ByteBuffer buffer = ByteBuffer.wrap(data);
key.interestOps(SelectionKey.OP_READ);
try {
while (nrecvd < length) {
if (key.isReadable()) {
long n = channel.read(buffer);
if (n < 0) {
throw new EOFException();
}
nrecvd += (int) n;
if (nrecvd < length && System.nanoTime() - startTime >= timeout.toNanos()) {
throw new SocketTimeoutException();
}
} else {
blockUntil(key);
}
}
} finally {
if (key.isValid()) {
key.interestOps(0);
}
}
return data;
}
private void blockUntil(SelectionKey key) throws IOException {
long remainingTimeout =
timeout.minus(System.nanoTime() - startTime, ChronoUnit.NANOS).toMillis();
int nkeys = 0;
if (remainingTimeout > 0) {
nkeys = key.selector().select(remainingTimeout);
} else if (remainingTimeout == 0) {
nkeys = key.selector().selectNow();
}
if (nkeys == 0) {
throw new SocketTimeoutException();
}
}
void cleanup() throws IOException {
key.selector().close();
key.channel().close();
}
byte[] recv() throws IOException {
byte[] buf = _recv(2);
int length = ((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF);
byte[] data = _recv(length);
SocketChannel channel = (SocketChannel) key.channel();
NioClient.verboseLog(
"TCP read",
channel.socket().getLocalSocketAddress(),
channel.socket().getRemoteSocketAddress(),
data);
return data;
}
}