com.barchart.udt.nio.SocketChannelUDT Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Network Show documentation
Show all versions of Network Show documentation
Encrypted, high-performance, and event-driven/reactive network stack for Java 11+
/**
* Copyright (C) 2009-2013 Barchart, Inc.
*
* All rights reserved. Licensed under the OSI BSD License.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package com.barchart.udt.nio;
import com.barchart.udt.ExceptionUDT;
import com.barchart.udt.SocketUDT;
import com.barchart.udt.TypeUDT;
import com.barchart.udt.anno.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.SocketChannel;
import java.nio.channels.UnresolvedAddressException;
/**
* {@link SocketChannel}-like wrapper for {@link SocketUDT}, can be either
* stream or message oriented, depending on {@link TypeUDT}
*
* The UDT socket that this SocketChannel wraps will be switched to blocking
* mode since this is the default for all SocketChannels on construction. If you
* require non-blocking functionality, you will need to call configureBlocking
* on the constructed SocketChannel class.
*
* you must use {@link SelectorProviderUDT#openSocketChannel()} to obtain
* instance of this class; do not use JDK
* {@link SocketChannel#open()};
*
* example:
*
*
* SelectorProvider provider = SelectorProviderUDT.DATAGRAM;
* SocketChannel clientChannel = provider.openSocketChannel();
* clientChannel.configureBlocking(true);
* Socket clientSocket = clientChannel.socket();
* InetSocketAddress clientAddress = new InetSocketAddress("localhost", 10000);
* clientSocket.bind(clientAddress);
* assert clientSocket.isBound();
* InetSocketAddress serverAddress = new InetSocketAddress("localhost", 12345);
* clientChannel.connect(serverAddress);
* assert clientSocket.isConnected();
*
*/
public class SocketChannelUDT extends SocketChannel implements ChannelUDT {
protected static final Logger log = LoggerFactory
.getLogger(SocketChannelUDT.class);
protected final Object connectLock = new Object();
/**
* local volatile variable, which mirrors super.blocking, to avoid the cost
* of synchronized call inside isBlocking()
*/
protected volatile boolean isBlockingMode = isBlocking();
protected volatile boolean isConnectFinished;
protected volatile boolean isConnectionPending;
@ThreadSafe("this")
protected NioSocketUDT socketAdapter;
protected final SocketUDT socketUDT;
private InetSocketAddress remoteSocket;
protected SocketChannelUDT( //
final SelectorProviderUDT provider, //
final SocketUDT socketUDT //
) throws ExceptionUDT {
super(provider);
this.socketUDT = socketUDT;
this.socketUDT.setBlocking(true);
}
protected SocketChannelUDT( //
final SelectorProviderUDT provider, //
final SocketUDT socketUDT, //
final boolean isConnected //
) throws ExceptionUDT {
this(provider, socketUDT);
if (isConnected) {
isConnectFinished = true;
isConnectionPending = false;
} else {
isConnectFinished = false;
isConnectionPending = true;
}
}
@Override
public boolean connect(final SocketAddress remote) throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
if (isConnected()) {
log.warn("already connected; ignoring remote={}", remote);
return true;
}
if (remote == null) {
close();
log.error("remote == null");
throw new NullPointerException();
}
remoteSocket = (InetSocketAddress) remote;
if (remoteSocket.isUnresolved()) {
log.error("can not use unresolved address: remote={}", remote);
close();
throw new UnresolvedAddressException();
}
if (isBlocking()) {
synchronized (connectLock) {
try {
if (isConnectionPending) {
close();
throw new ConnectionPendingException();
}
isConnectionPending = true;
begin();
socketUDT.connect(remoteSocket);
} finally {
end(true);
isConnectionPending = false;
connectLock.notifyAll();
}
}
return socketUDT.isConnected();
} else {
/** non Blocking */
if (!isRegistered()) {
/** this channel is independent of any selector */
log.error("UDT channel is in NON blocking mode; "
+ "must register with a selector " //
+ "before trying to connect(); " //
+ "socketId=" + socketUDT.id());
throw new IllegalBlockingModeException();
}
/** this channel is registered with a selector */
synchronized (connectLock) {
if (isConnectionPending) {
close();
log.error("connection already in progress");
throw new ConnectionPendingException();
}
isConnectFinished = false;
isConnectionPending = true;
socketUDT.connect(remoteSocket);
}
/**
* connection operation must later be completed by invoking the
* #finishConnect() method.
*/
return false;
}
}
@Override
public boolean finishConnect() throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
if (isBlocking()) {
synchronized (connectLock) {
while (isConnectionPending) {
try {
connectLock.wait();
} catch (final InterruptedException e) {
throw new IOException(e);
}
}
}
}
if (isConnected()) {
isConnectFinished = true;
isConnectionPending = false;
return true;
} else {
log.error("connect failure : {}", socketUDT);
throw new IOException();
}
}
@Override
protected void implCloseSelectableChannel() throws IOException {
socketUDT.close();
}
@Override
protected void implConfigureBlocking(final boolean block)
throws IOException {
socketUDT.setBlocking(block);
isBlockingMode = block;
}
@Override
public boolean isConnected() {
return socketUDT.isConnected();
}
@Override
public boolean isConnectFinished() {
return isConnectFinished;
}
@Override
public boolean isConnectionPending() {
return isConnectionPending;
}
@Override
public KindUDT kindUDT() {
return KindUDT.CONNECTOR;
}
@Override
public SelectorProviderUDT providerUDT() {
return (SelectorProviderUDT) super.provider();
}
//
/**
* See {@link SocketChannel#read(ByteBuffer)} contract;
* note: this method does not return (-1) as EOS (end of stream flag)
*
* @return <0 should not happen
* =0 blocking mode: timeout occurred on receive
* =0 non-blocking mode: nothing is received by the
* underlying UDT socket
* >0 actual bytes received count
* @see SocketUDT#receive(ByteBuffer)
* @see SocketUDT#receive(byte[], int, int)
*/
@Override
public int read(final ByteBuffer buffer) throws IOException {
final int remaining = buffer.remaining();
if (remaining <= 0) {
return 0;
}
final SocketUDT socket = socketUDT;
final boolean isBlocking = isBlockingMode;
final int sizeReceived;
try {
if (isBlocking) {
begin(); // JDK contract for NIO blocking calls
}
if (buffer.isDirect()) {
sizeReceived = socket.receive(buffer);
} else {
final byte[] array = buffer.array();
final int position = buffer.position();
final int limit = buffer.limit();
sizeReceived = socket.receive(array, position, limit);
if (0 < sizeReceived && sizeReceived <= remaining) {
buffer.position(position + sizeReceived);
}
}
} finally {
if (isBlocking) {
end(true); // JDK contract for NIO blocking calls
}
}
// see contract for receive()
if (sizeReceived < 0) {
// log.trace("nothing was received; socket={}", socket);
return 0;
}
if (sizeReceived == 0) {
// log.trace("receive timeout; socket={}", socket);
return 0;
}
if (sizeReceived <= remaining) {
return sizeReceived;
} else {
log.error("should not happen: socket={}", socket);
return 0;
}
}
@Override
public long read(final ByteBuffer[] dsts, final int offset, final int length)
throws IOException {
throw new RuntimeException("feature not available");
}
@Override
public synchronized NioSocketUDT socket() {
if (socketAdapter == null) {
try {
socketAdapter = new NioSocketUDT(this);
} catch (final ExceptionUDT e) {
log.error("failed to make socket", e);
}
}
return socketAdapter;
}
@Override
public SocketUDT socketUDT() {
return socketUDT;
}
@Override
public String toString() {
return socketUDT.toString();
}
/**
* See {@link SocketChannel#write(ByteBuffer)} contract;
*
* @return <0 should not happen
* =0 blocking mode: timeout occurred on send
* =0 non-blocking mode: buffer is full in the
* underlying UDT socket; nothing is sent
* >0 actual bytes sent count
* @see SocketUDT#send(ByteBuffer)
* @see SocketUDT#send(byte[], int, int)
*/
@Override
public int write(final ByteBuffer buffer) throws IOException {
// writeCount.incrementAndGet();
if (buffer == null) {
throw new NullPointerException("buffer == null");
}
final int remaining = buffer.remaining();
if (remaining <= 0) {
return 0;
}
final SocketUDT socket = socketUDT;
final boolean isBlocking = isBlockingMode;
int sizeSent = 0;
int ret = 0;
try {
if (isBlocking) {
begin(); // JDK contract for NIO blocking calls
}
if (buffer.isDirect()) {
do {
ret = socket.send(buffer);
if (ret > 0)
sizeSent += ret;
} while (buffer.hasRemaining() && isBlocking);
} else {
final byte[] array = buffer.array();
int position = buffer.position();
final int limit = buffer.limit();
do {
ret = socket.send(array, position, limit);
if (0 < ret && ret <= remaining) {
sizeSent += ret;
position += ret;
buffer.position(position);
}
} while (buffer.hasRemaining() && isBlocking);
}
} finally {
if (isBlocking) {
end(true); // JDK contract for NIO blocking calls
}
}
// see contract for send()
if (ret < 0) {
// log.trace("no buffer space; socket={}", socket);
return 0;
}
if (ret == 0) {
// log.trace("send timeout; socket={}", socket);
return 0;
}
if (sizeSent <= remaining) {
return sizeSent;
} else {
log.error("should not happen; socket={}", socket);
return 0;
}
}
@Override
public long write(final ByteBuffer[] bufferArray, final int offset,
final int length) throws IOException {
try {
long total = 0;
for (int index = offset; index < offset + length; index++) {
final ByteBuffer buffer = bufferArray[index];
final int remaining = buffer.remaining();
final int processed = write(buffer);
if (remaining == processed) {
total += processed;
} else {
throw new IllegalStateException(
"failed to write buffer in array");
}
}
return total;
} catch (final Throwable e) {
throw new IOException("failed to write buffer array", e);
}
}
@Override
public TypeUDT typeUDT() {
return providerUDT().type();
}
/** java 7 */
public SocketChannelUDT bind(final SocketAddress localAddress)
throws IOException {
socketUDT.bind((InetSocketAddress) localAddress);
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy