
com.barchart.udt.nio.ChannelSocketUDT Maven / Gradle / Ivy
/**
* Copyright (C) 2009-2012 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 java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
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;
import java.nio.channels.spi.SelectorProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.barchart.udt.SocketUDT;
/**
* you must use {@link SelectorProviderUDT#openSocketChannel()} to obtain
* instance of this class; do not use JDK
* {@link java.nio.channels.SocketChannel#open()};
*
* example:
*
* [code]
*
* 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();
*
* [/code]
*/
public class ChannelSocketUDT extends SocketChannel implements ChannelUDT {
private static final Logger log = LoggerFactory
.getLogger(ChannelSocketUDT.class);
final SocketUDT socketUDT;
ChannelSocketUDT(SelectorProvider provider, SocketUDT socketUDT) {
super(provider);
this.socketUDT = socketUDT;
}
@Override
protected void implCloseSelectableChannel() throws IOException {
socketUDT.close();
}
/*
* local volatile variable, which mirrors super.blocking, to avoid the cost
* of synchronized call inside isBlocking()
*/
private volatile boolean isBlockingMode = isBlocking();
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
socketUDT.configureBlocking(block);
isBlockingMode = block;
}
private volatile boolean isConnectionPending;
@Override
public boolean connect(SocketAddress remote) throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
if (isConnected()) {
log.warn("already connected; ignoring remote={}", remote);
return true;
}
if (remote == null) {
close();
throw new NullPointerException("remote == null");
}
InetSocketAddress remoteSocket = (InetSocketAddress) remote;
if (remoteSocket.isUnresolved()) {
log.error("can not use unresolved address: remote={}", remote);
close();
throw new UnresolvedAddressException();
}
if (isBlocking()) {
if (isConnectionPending) {
close();
throw new ConnectionPendingException();
} else {
isConnectionPending = true;
}
try {
begin();
socketUDT.connect(remoteSocket);
} finally {
end(true);
}
isConnectionPending = false;
return socketUDT.isConnected();
} else { // non Blocking
final SelectionKeyUDT key = channelKey;
if (key == null) {
// 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()");
throw new IllegalBlockingModeException();
} else {
// this channel is registered with a selector
key.selectorUDT.submitConnectRequest(key, remoteSocket);
/*
* connection operation must later be completed by invoking the
* finishConnect() method.
*/
return false;
}
}
}
/**
* note this is redundant for blocking mode
*/
@Override
public boolean finishConnect() throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
if (isBlocking()) {
return isConnected();
} else { // non blocking
if (isConnected()) {
logStatus();
return true;
} else {
IOException exception = connectException;
if (exception == null) {
return false;
} else {
throw exception;
}
}
}
}
@Override
public boolean isConnected() {
return socketUDT.isConnected();
}
@Override
public boolean isConnectionPending() {
throw new RuntimeException("feature not available");
}
/**
* See {@link java.nio.channels.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 com.barchart.udt.SocketUDT#receive(ByteBuffer)
* @see com.barchart.udt.SocketUDT#receive(byte[], int, int)
*/
@Override
public final int read(ByteBuffer buffer) throws IOException {
if (buffer == null) {
throw new NullPointerException("buffer == null");
}
final int remaining = buffer.remaining();
if (remaining > 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 {
assert buffer.hasArray();
byte[] array = buffer.array();
int position = buffer.position();
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; socketID={}",
socket.getSocketId());
return 0;
}
if (sizeReceived == 0) {
log.trace("receive timeout; socketID={}", socket.getSocketId());
return 0;
}
if (sizeReceived <= remaining) {
return sizeReceived;
} else { // should not happen
log.error("unexpected: sizeReceived > remaining; socketID={}",
socket.getSocketId());
return 0;
}
} else {
return 0;
}
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException {
throw new RuntimeException("feature not available");
}
// guarded by 'this'
protected Socket socketAdapter;
@Override
public Socket socket() {
synchronized (this) {
if (socketAdapter == null) {
socketAdapter = new AdapterSocketUDT(this, socketUDT);
}
return socketAdapter;
}
}
//
// used for debugging
// private final AtomicInteger writeCount = new AtomicInteger(0);
// private final AtomicInteger writeSize = new AtomicInteger(0);
/**
* See {@link java.nio.channels.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 com.barchart.udt.SocketUDT#send(ByteBuffer)
* @see com.barchart.udt.SocketUDT#send(byte[], int, int)
*/
@Override
public final int write(ByteBuffer buffer) throws IOException {
// writeCount.incrementAndGet();
if (buffer == null) {
throw new NullPointerException("buffer == null");
}
final int remaining = buffer.remaining();
if (remaining > 0) {
final SocketUDT socket = socketUDT;
final boolean isBlocking = isBlockingMode;
final int sizeSent;
try {
if (isBlocking) {
begin(); // JDK contract for NIO blocking calls
}
if (buffer.isDirect()) {
sizeSent = socket.send(buffer);
} else {
assert buffer.hasArray();
byte[] array = buffer.array();
int position = buffer.position();
int limit = buffer.limit();
sizeSent = socket.send(array, position, limit);
if (0 < sizeSent && sizeSent <= remaining) {
buffer.position(position + sizeSent);
}
}
} finally {
if (isBlocking) {
end(true); // JDK contract for NIO blocking calls
}
}
// see contract for send()
if (sizeSent < 0) {
log.trace("no buffer space for send; socketID={}",
socket.getSocketId());
// logStatus();
// log.info("writeCount={} writeSize={}", writeCount,
// writeSize);
// System.exit(1);
return 0;
}
if (sizeSent == 0) {
log.trace("send timeout; socketID={}", socket.getSocketId());
return 0;
}
if (sizeSent <= remaining) {
// writeSize.addAndGet(sizeSent);
return sizeSent;
} else { // should not happen
log.error("unexpected: sizeSent > remaining; socketID={}",
socket.getSocketId());
return 0;
}
} else {
return 0;
}
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException {
throw new RuntimeException("feature not available");
}
@Override
public SocketUDT getSocketUDT() {
return socketUDT;
}
@Override
public KindUDT getChannelKind() {
return KindUDT.CONNECTOR;
}
// note: 1<->1 mapping of channels and keys
protected volatile SelectionKeyUDT channelKey;
protected void setRegistredKey(SelectionKeyUDT key) {
// one time init
assert channelKey == null;
channelKey = key;
}
// set by connector task
protected volatile IOException connectException;
protected void setConnectException(IOException exception) {
this.connectException = exception;
}
@Override
public boolean isOpenSocketUDT() {
return socketUDT.isOpen();
}
//
@Override
public String toString() {
return socketUDT.toString();
}
void logStatus() {
if (channelKey != null) {
log.debug("channelKey={}", channelKey);
}
log.debug("options={}", socketUDT.toStringOptions());
log.debug("monitor={}", socketUDT.toStringMonitor());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy