package net.openhft.chronicle.network;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.util.Time;
import net.openhft.chronicle.network.connection.FatalFailureMonitor;
import net.openhft.chronicle.network.connection.SocketAddressSupplier;
import net.openhft.chronicle.wire.Marshallable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import static net.openhft.chronicle.core.io.Closeable.closeQuietly;
public interface ConnectionStrategy extends Marshallable {
/**
* @param name the name of the connection, only used for logging
* @param socketAddressSupplier
* @param networkStatsListener
* @param didLogIn was the last attempt successfull, was a login established
* @param fatalFailureMonitor
* @return
* @throws InterruptedException
*/
SocketChannel connect(@NotNull String name,
@NotNull SocketAddressSupplier socketAddressSupplier,
@Nullable NetworkStatsListener<? extends NetworkContext> networkStatsListener,
boolean didLogIn,
@Nullable FatalFailureMonitor fatalFailureMonitor) throws InterruptedException;
/**
* the reason for this method is that unlike the selector it uses tick time
*/
@Nullable
default SocketChannel openSocketChannel(@NotNull InetSocketAddress socketAddress,
int tcpBufferSize,
long timeoutMs) throws IOException, InterruptedException {
assert timeoutMs > 0;
long start = Time.tickTime();
SocketChannel sc = socketChannel(socketAddress, tcpBufferSize);
if (sc != null)
return sc;
for (; ; ) {
if (Thread.currentThread().isInterrupted())
throw new InterruptedException();
if (start + timeoutMs < Time.tickTime()) {
Jvm.warn().on(ConnectionStrategy.class, "Timed out attempting to connect to " + socketAddress);
return null;
}
sc = socketChannel(socketAddress, tcpBufferSize);
if (sc != null)
return sc;
Thread.yield();
}
}
@Nullable
static SocketChannel socketChannel(@NotNull InetSocketAddress socketAddress, int tcpBufferSize) throws IOException {
final SocketChannel result = SocketChannel.open();
@Nullable Selector selector = null;
boolean failed = true;
try {
result.configureBlocking(false);
Socket socket = result.socket();
socket.setTcpNoDelay(true);
socket.setReceiveBufferSize(tcpBufferSize);
socket.setSendBufferSize(tcpBufferSize);
socket.setSoTimeout(0);
socket.setSoLinger(false, 0);
result.connect(socketAddress);
selector = Selector.open();
result.register(selector, SelectionKey.OP_CONNECT);
int select = selector.select(1);
if (select == 0) {
Jvm.debug().on(ConnectionStrategy.class, "Timed out attempting to connect to " + socketAddress);
return null;
} else {
try {
if (!result.finishConnect())
return null;
} catch (IOException e) {
Jvm.debug().on(ConnectionStrategy.class, "Failed to connect to " + socketAddress + " " + e);
return null;
}
}
failed = false;
return result;
} catch (Exception e) {
return null;
} finally {
closeQuietly(selector);
if (failed)
closeQuietly(result);
}
}
}