|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.nio.ch; |
|
|
|
import java.io.FileDescriptor; |
|
import java.io.IOException; |
|
import java.net.InetAddress; |
|
import java.net.Inet4Address; |
|
import java.net.InetSocketAddress; |
|
import java.net.ProtocolFamily; |
|
import java.net.Socket; |
|
import java.net.SocketAddress; |
|
import java.net.SocketException; |
|
import java.net.SocketOption; |
|
import java.net.SocketTimeoutException; |
|
import java.net.StandardSocketOptions; |
|
import java.nio.ByteBuffer; |
|
import java.nio.channels.AlreadyBoundException; |
|
import java.nio.channels.AlreadyConnectedException; |
|
import java.nio.channels.AsynchronousCloseException; |
|
import java.nio.channels.ClosedChannelException; |
|
import java.nio.channels.ConnectionPendingException; |
|
import java.nio.channels.IllegalBlockingModeException; |
|
import java.nio.channels.NoConnectionPendingException; |
|
import java.nio.channels.NotYetConnectedException; |
|
import java.nio.channels.SelectionKey; |
|
import java.nio.channels.SocketChannel; |
|
import java.nio.channels.spi.SelectorProvider; |
|
import java.nio.file.Path; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.Set; |
|
import java.util.Objects; |
|
import java.util.concurrent.locks.ReentrantLock; |
|
import static java.net.StandardProtocolFamily.INET; |
|
import static java.net.StandardProtocolFamily.INET6; |
|
import static java.net.StandardProtocolFamily.UNIX; |
|
|
|
import sun.net.ConnectionResetException; |
|
import sun.net.NetHooks; |
|
import sun.net.ext.ExtendedSocketOptions; |
|
import sun.net.util.SocketExceptions; |
|
|
|
/** |
|
* An implementation of SocketChannels |
|
*/ |
|
|
|
class SocketChannelImpl |
|
extends SocketChannel |
|
implements SelChImpl |
|
{ |
|
|
|
private static final NativeDispatcher nd = new SocketDispatcher(); |
|
|
|
|
|
private final ProtocolFamily family; |
|
|
|
|
|
private final FileDescriptor fd; |
|
private final int fdVal; |
|
|
|
|
|
private final ReentrantLock readLock = new ReentrantLock(); |
|
|
|
|
|
private final ReentrantLock writeLock = new ReentrantLock(); |
|
|
|
// Lock held by any thread that modifies the state fields declared below |
|
|
|
private final Object stateLock = new Object(); |
|
|
|
|
|
private volatile boolean isInputClosed; |
|
private volatile boolean isOutputClosed; |
|
|
|
|
|
private boolean connectionReset; |
|
|
|
// -- The following fields are protected by stateLock |
|
|
|
|
|
private boolean isReuseAddress; |
|
|
|
|
|
private static final int ST_UNCONNECTED = 0; |
|
private static final int ST_CONNECTIONPENDING = 1; |
|
private static final int ST_CONNECTED = 2; |
|
private static final int ST_CLOSING = 3; |
|
private static final int ST_CLOSED = 4; |
|
private volatile int state; |
|
|
|
|
|
private long readerThread; |
|
private long writerThread; |
|
|
|
|
|
private SocketAddress localAddress; |
|
private SocketAddress remoteAddress; |
|
|
|
|
|
private Socket socket; |
|
|
|
// -- End of fields protected by stateLock |
|
|
|
SocketChannelImpl(SelectorProvider sp) throws IOException { |
|
this(sp, Net.isIPv6Available() ? INET6 : INET); |
|
} |
|
|
|
SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException { |
|
super(sp); |
|
Objects.requireNonNull(family, "'family' is null"); |
|
if ((family != INET) && (family != INET6) && (family != UNIX)) { |
|
throw new UnsupportedOperationException("Protocol family not supported"); |
|
} |
|
if (family == INET6 && !Net.isIPv6Available()) { |
|
throw new UnsupportedOperationException("IPv6 not available"); |
|
} |
|
|
|
this.family = family; |
|
if (family == UNIX) { |
|
this.fd = UnixDomainSockets.socket(); |
|
} else { |
|
this.fd = Net.socket(family, true); |
|
} |
|
this.fdVal = IOUtil.fdVal(fd); |
|
} |
|
|
|
// Constructor for sockets obtained from server sockets |
|
|
|
SocketChannelImpl(SelectorProvider sp, |
|
ProtocolFamily family, |
|
FileDescriptor fd, |
|
SocketAddress remoteAddress) |
|
throws IOException |
|
{ |
|
super(sp); |
|
this.family = family; |
|
this.fd = fd; |
|
this.fdVal = IOUtil.fdVal(fd); |
|
synchronized (stateLock) { |
|
if (family == UNIX) { |
|
this.localAddress = UnixDomainSockets.localAddress(fd); |
|
} else { |
|
this.localAddress = Net.localAddress(fd); |
|
} |
|
this.remoteAddress = remoteAddress; |
|
this.state = ST_CONNECTED; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean isNetSocket() { |
|
return (family == INET) || (family == INET6); |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean isUnixSocket() { |
|
return (family == UNIX); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void ensureOpen() throws ClosedChannelException { |
|
if (!isOpen()) |
|
throw new ClosedChannelException(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void ensureOpenAndConnected() throws ClosedChannelException { |
|
int state = this.state; |
|
if (state < ST_CONNECTED) { |
|
throw new NotYetConnectedException(); |
|
} else if (state > ST_CONNECTED) { |
|
throw new ClosedChannelException(); |
|
} |
|
} |
|
|
|
@Override |
|
public Socket socket() { |
|
synchronized (stateLock) { |
|
if (socket == null) { |
|
if (isNetSocket()) { |
|
socket = SocketAdaptor.create(this); |
|
} else { |
|
throw new UnsupportedOperationException("Not supported"); |
|
} |
|
} |
|
return socket; |
|
} |
|
} |
|
|
|
@Override |
|
public SocketAddress getLocalAddress() throws IOException { |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (isUnixSocket()) { |
|
return UnixDomainSockets.getRevealedLocalAddress(localAddress); |
|
} else { |
|
return Net.getRevealedLocalAddress(localAddress); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public SocketAddress getRemoteAddress() throws IOException { |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
return remoteAddress; |
|
} |
|
} |
|
|
|
@Override |
|
public <T> SocketChannel setOption(SocketOption<T> name, T value) |
|
throws IOException |
|
{ |
|
Objects.requireNonNull(name); |
|
if (!supportedOptions().contains(name)) |
|
throw new UnsupportedOperationException("'" + name + "' not supported"); |
|
if (!name.type().isInstance(value)) |
|
throw new IllegalArgumentException("Invalid value '" + value + "'"); |
|
|
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
|
|
if (isNetSocket()) { |
|
if (name == StandardSocketOptions.IP_TOS) { |
|
|
|
Net.setSocketOption(fd, family, name, value); |
|
return this; |
|
} |
|
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) { |
|
|
|
isReuseAddress = (Boolean) value; |
|
return this; |
|
} |
|
} |
|
|
|
|
|
Net.setSocketOption(fd, name, value); |
|
return this; |
|
} |
|
} |
|
|
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public <T> T getOption(SocketOption<T> name) |
|
throws IOException |
|
{ |
|
Objects.requireNonNull(name); |
|
if (!supportedOptions().contains(name)) |
|
throw new UnsupportedOperationException("'" + name + "' not supported"); |
|
|
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
|
|
if (isNetSocket()) { |
|
if (name == StandardSocketOptions.IP_TOS) { |
|
|
|
return (T) Net.getSocketOption(fd, family, name); |
|
} |
|
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) { |
|
|
|
return (T) Boolean.valueOf(isReuseAddress); |
|
} |
|
} |
|
|
|
|
|
return (T) Net.getSocketOption(fd, name); |
|
} |
|
} |
|
|
|
private static class DefaultOptionsHolder { |
|
static final Set<SocketOption<?>> defaultInetOptions = defaultInetOptions(); |
|
static final Set<SocketOption<?>> defaultUnixOptions = defaultUnixOptions(); |
|
|
|
private static Set<SocketOption<?>> defaultInetOptions() { |
|
HashSet<SocketOption<?>> set = new HashSet<>(); |
|
set.add(StandardSocketOptions.SO_SNDBUF); |
|
set.add(StandardSocketOptions.SO_RCVBUF); |
|
set.add(StandardSocketOptions.SO_KEEPALIVE); |
|
set.add(StandardSocketOptions.SO_REUSEADDR); |
|
if (Net.isReusePortAvailable()) { |
|
set.add(StandardSocketOptions.SO_REUSEPORT); |
|
} |
|
set.add(StandardSocketOptions.SO_LINGER); |
|
set.add(StandardSocketOptions.TCP_NODELAY); |
|
|
|
set.add(StandardSocketOptions.IP_TOS); |
|
set.add(ExtendedSocketOption.SO_OOBINLINE); |
|
set.addAll(ExtendedSocketOptions.clientSocketOptions()); |
|
return Collections.unmodifiableSet(set); |
|
} |
|
|
|
private static Set<SocketOption<?>> defaultUnixOptions() { |
|
HashSet<SocketOption<?>> set = new HashSet<>(); |
|
set.add(StandardSocketOptions.SO_SNDBUF); |
|
set.add(StandardSocketOptions.SO_RCVBUF); |
|
set.add(StandardSocketOptions.SO_LINGER); |
|
set.addAll(ExtendedSocketOptions.unixDomainSocketOptions()); |
|
return Collections.unmodifiableSet(set); |
|
} |
|
} |
|
|
|
@Override |
|
public final Set<SocketOption<?>> supportedOptions() { |
|
if (isUnixSocket()) { |
|
return DefaultOptionsHolder.defaultUnixOptions; |
|
} else { |
|
return DefaultOptionsHolder.defaultInetOptions; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void beginRead(boolean blocking) throws ClosedChannelException { |
|
if (blocking) { |
|
|
|
begin(); |
|
|
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
|
|
readerThread = NativeThread.current(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endRead(boolean blocking, boolean completed) |
|
throws AsynchronousCloseException |
|
{ |
|
if (blocking) { |
|
synchronized (stateLock) { |
|
readerThread = 0; |
|
if (state == ST_CLOSING) { |
|
tryFinishClose(); |
|
} |
|
} |
|
|
|
end(completed); |
|
} |
|
} |
|
|
|
private void throwConnectionReset() throws SocketException { |
|
throw new SocketException("Connection reset"); |
|
} |
|
|
|
@Override |
|
public int read(ByteBuffer buf) throws IOException { |
|
Objects.requireNonNull(buf); |
|
|
|
readLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
boolean blocking = isBlocking(); |
|
int n = 0; |
|
try { |
|
beginRead(blocking); |
|
|
|
|
|
if (connectionReset) |
|
throwConnectionReset(); |
|
|
|
|
|
if (isInputClosed) |
|
return IOStatus.EOF; |
|
|
|
n = IOUtil.read(fd, buf, -1, nd); |
|
if (blocking) { |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLIN); |
|
n = IOUtil.read(fd, buf, -1, nd); |
|
} |
|
} |
|
} catch (ConnectionResetException e) { |
|
connectionReset = true; |
|
throwConnectionReset(); |
|
} finally { |
|
endRead(blocking, n > 0); |
|
if (n <= 0 && isInputClosed) |
|
return IOStatus.EOF; |
|
} |
|
return IOStatus.normalize(n); |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} |
|
|
|
@Override |
|
public long read(ByteBuffer[] dsts, int offset, int length) |
|
throws IOException |
|
{ |
|
Objects.checkFromIndexSize(offset, length, dsts.length); |
|
|
|
readLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
boolean blocking = isBlocking(); |
|
long n = 0; |
|
try { |
|
beginRead(blocking); |
|
|
|
|
|
if (connectionReset) |
|
throwConnectionReset(); |
|
|
|
|
|
if (isInputClosed) |
|
return IOStatus.EOF; |
|
|
|
n = IOUtil.read(fd, dsts, offset, length, nd); |
|
if (blocking) { |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLIN); |
|
n = IOUtil.read(fd, dsts, offset, length, nd); |
|
} |
|
} |
|
} catch (ConnectionResetException e) { |
|
connectionReset = true; |
|
throwConnectionReset(); |
|
} finally { |
|
endRead(blocking, n > 0); |
|
if (n <= 0 && isInputClosed) |
|
return IOStatus.EOF; |
|
} |
|
return IOStatus.normalize(n); |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void beginWrite(boolean blocking) throws ClosedChannelException { |
|
if (blocking) { |
|
|
|
begin(); |
|
|
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (isOutputClosed) |
|
throw new ClosedChannelException(); |
|
|
|
writerThread = NativeThread.current(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endWrite(boolean blocking, boolean completed) |
|
throws AsynchronousCloseException |
|
{ |
|
if (blocking) { |
|
synchronized (stateLock) { |
|
writerThread = 0; |
|
if (state == ST_CLOSING) { |
|
tryFinishClose(); |
|
} |
|
} |
|
|
|
end(completed); |
|
} |
|
} |
|
|
|
@Override |
|
public int write(ByteBuffer buf) throws IOException { |
|
Objects.requireNonNull(buf); |
|
writeLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
boolean blocking = isBlocking(); |
|
int n = 0; |
|
try { |
|
beginWrite(blocking); |
|
n = IOUtil.write(fd, buf, -1, nd); |
|
if (blocking) { |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLOUT); |
|
n = IOUtil.write(fd, buf, -1, nd); |
|
} |
|
} |
|
} finally { |
|
endWrite(blocking, n > 0); |
|
if (n <= 0 && isOutputClosed) |
|
throw new AsynchronousCloseException(); |
|
} |
|
return IOStatus.normalize(n); |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} |
|
|
|
@Override |
|
public long write(ByteBuffer[] srcs, int offset, int length) |
|
throws IOException |
|
{ |
|
Objects.checkFromIndexSize(offset, length, srcs.length); |
|
|
|
writeLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
boolean blocking = isBlocking(); |
|
long n = 0; |
|
try { |
|
beginWrite(blocking); |
|
n = IOUtil.write(fd, srcs, offset, length, nd); |
|
if (blocking) { |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLOUT); |
|
n = IOUtil.write(fd, srcs, offset, length, nd); |
|
} |
|
} |
|
} finally { |
|
endWrite(blocking, n > 0); |
|
if (n <= 0 && isOutputClosed) |
|
throw new AsynchronousCloseException(); |
|
} |
|
return IOStatus.normalize(n); |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
int sendOutOfBandData(byte b) throws IOException { |
|
writeLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
boolean blocking = isBlocking(); |
|
int n = 0; |
|
try { |
|
beginWrite(blocking); |
|
if (blocking) { |
|
do { |
|
n = Net.sendOOB(fd, b); |
|
} while (n == IOStatus.INTERRUPTED && isOpen()); |
|
} else { |
|
n = Net.sendOOB(fd, b); |
|
} |
|
} finally { |
|
endWrite(blocking, n > 0); |
|
if (n <= 0 && isOutputClosed) |
|
throw new AsynchronousCloseException(); |
|
} |
|
return IOStatus.normalize(n); |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} |
|
|
|
@Override |
|
protected void implConfigureBlocking(boolean block) throws IOException { |
|
readLock.lock(); |
|
try { |
|
writeLock.lock(); |
|
try { |
|
lockedConfigureBlocking(block); |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void lockedConfigureBlocking(boolean block) throws IOException { |
|
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread(); |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
IOUtil.configureBlocking(fd, block); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean tryLockedConfigureBlocking(boolean block) throws IOException { |
|
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread(); |
|
synchronized (stateLock) { |
|
if (isOpen()) { |
|
IOUtil.configureBlocking(fd, block); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
SocketAddress localAddress() { |
|
synchronized (stateLock) { |
|
return localAddress; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
SocketAddress remoteAddress() { |
|
synchronized (stateLock) { |
|
return remoteAddress; |
|
} |
|
} |
|
|
|
@Override |
|
public SocketChannel bind(SocketAddress local) throws IOException { |
|
readLock.lock(); |
|
try { |
|
writeLock.lock(); |
|
try { |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (state == ST_CONNECTIONPENDING) |
|
throw new ConnectionPendingException(); |
|
if (localAddress != null) |
|
throw new AlreadyBoundException(); |
|
if (isUnixSocket()) { |
|
localAddress = unixBind(local); |
|
} else { |
|
localAddress = netBind(local); |
|
} |
|
} |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
return this; |
|
} |
|
|
|
private SocketAddress unixBind(SocketAddress local) throws IOException { |
|
UnixDomainSockets.checkPermission(); |
|
if (local == null) { |
|
return UnixDomainSockets.UNNAMED; |
|
} else { |
|
Path path = UnixDomainSockets.checkAddress(local).getPath(); |
|
if (path.toString().isEmpty()) { |
|
return UnixDomainSockets.UNNAMED; |
|
} else { |
|
|
|
UnixDomainSockets.bind(fd, path); |
|
return UnixDomainSockets.localAddress(fd); |
|
} |
|
} |
|
} |
|
|
|
private SocketAddress netBind(SocketAddress local) throws IOException { |
|
InetSocketAddress isa; |
|
if (local == null) { |
|
isa = new InetSocketAddress(Net.anyLocalAddress(family), 0); |
|
} else { |
|
isa = Net.checkAddress(local, family); |
|
} |
|
@SuppressWarnings("removal") |
|
SecurityManager sm = System.getSecurityManager(); |
|
if (sm != null) { |
|
sm.checkListen(isa.getPort()); |
|
} |
|
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); |
|
Net.bind(family, fd, isa.getAddress(), isa.getPort()); |
|
return Net.localAddress(fd); |
|
} |
|
|
|
@Override |
|
public boolean isConnected() { |
|
return (state == ST_CONNECTED); |
|
} |
|
|
|
@Override |
|
public boolean isConnectionPending() { |
|
return (state == ST_CONNECTIONPENDING); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void beginConnect(boolean blocking, SocketAddress sa) |
|
throws IOException |
|
{ |
|
if (blocking) { |
|
|
|
begin(); |
|
} |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
int state = this.state; |
|
if (state == ST_CONNECTED) |
|
throw new AlreadyConnectedException(); |
|
if (state == ST_CONNECTIONPENDING) |
|
throw new ConnectionPendingException(); |
|
assert state == ST_UNCONNECTED; |
|
this.state = ST_CONNECTIONPENDING; |
|
|
|
if (isNetSocket() && (localAddress == null)) { |
|
InetSocketAddress isa = (InetSocketAddress) sa; |
|
NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort()); |
|
} |
|
remoteAddress = sa; |
|
|
|
if (blocking) { |
|
|
|
readerThread = NativeThread.current(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endConnect(boolean blocking, boolean completed) |
|
throws IOException |
|
{ |
|
endRead(blocking, completed); |
|
|
|
if (completed) { |
|
synchronized (stateLock) { |
|
if (state == ST_CONNECTIONPENDING) { |
|
if (isUnixSocket()) { |
|
localAddress = UnixDomainSockets.localAddress(fd); |
|
} else { |
|
localAddress = Net.localAddress(fd); |
|
} |
|
state = ST_CONNECTED; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private SocketAddress checkRemote(SocketAddress sa) { |
|
if (isUnixSocket()) { |
|
UnixDomainSockets.checkPermission(); |
|
return UnixDomainSockets.checkAddress(sa); |
|
} else { |
|
InetSocketAddress isa = Net.checkAddress(sa, family); |
|
@SuppressWarnings("removal") |
|
SecurityManager sm = System.getSecurityManager(); |
|
if (sm != null) { |
|
sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); |
|
} |
|
InetAddress address = isa.getAddress(); |
|
if (address.isAnyLocalAddress()) { |
|
int port = isa.getPort(); |
|
if (address instanceof Inet4Address) { |
|
return new InetSocketAddress(Net.inet4LoopbackAddress(), port); |
|
} else { |
|
assert family == INET6; |
|
return new InetSocketAddress(Net.inet6LoopbackAddress(), port); |
|
} |
|
} else { |
|
return isa; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public boolean connect(SocketAddress remote) throws IOException { |
|
SocketAddress sa = checkRemote(remote); |
|
try { |
|
readLock.lock(); |
|
try { |
|
writeLock.lock(); |
|
try { |
|
boolean blocking = isBlocking(); |
|
boolean connected = false; |
|
try { |
|
beginConnect(blocking, sa); |
|
int n; |
|
if (isUnixSocket()) { |
|
n = UnixDomainSockets.connect(fd, sa); |
|
} else { |
|
n = Net.connect(family, fd, sa); |
|
} |
|
if (n > 0) { |
|
connected = true; |
|
} else if (blocking) { |
|
assert IOStatus.okayToRetry(n); |
|
boolean polled = false; |
|
while (!polled && isOpen()) { |
|
park(Net.POLLOUT); |
|
polled = Net.pollConnectNow(fd); |
|
} |
|
connected = polled && isOpen(); |
|
} |
|
} finally { |
|
endConnect(blocking, connected); |
|
} |
|
return connected; |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} catch (IOException ioe) { |
|
|
|
close(); |
|
throw SocketExceptions.of(ioe, sa); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void beginFinishConnect(boolean blocking) throws ClosedChannelException { |
|
if (blocking) { |
|
|
|
begin(); |
|
} |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (state != ST_CONNECTIONPENDING) |
|
throw new NoConnectionPendingException(); |
|
if (blocking) { |
|
|
|
readerThread = NativeThread.current(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endFinishConnect(boolean blocking, boolean completed) |
|
throws IOException |
|
{ |
|
endRead(blocking, completed); |
|
|
|
if (completed) { |
|
synchronized (stateLock) { |
|
if (state == ST_CONNECTIONPENDING) { |
|
if (isUnixSocket()) { |
|
localAddress = UnixDomainSockets.localAddress(fd); |
|
} else { |
|
localAddress = Net.localAddress(fd); |
|
} |
|
state = ST_CONNECTED; |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public boolean finishConnect() throws IOException { |
|
try { |
|
readLock.lock(); |
|
try { |
|
writeLock.lock(); |
|
try { |
|
|
|
if (isConnected()) |
|
return true; |
|
|
|
boolean blocking = isBlocking(); |
|
boolean connected = false; |
|
try { |
|
beginFinishConnect(blocking); |
|
boolean polled = Net.pollConnectNow(fd); |
|
if (blocking) { |
|
while (!polled && isOpen()) { |
|
park(Net.POLLOUT); |
|
polled = Net.pollConnectNow(fd); |
|
} |
|
} |
|
connected = polled && isOpen(); |
|
} finally { |
|
endFinishConnect(blocking, connected); |
|
} |
|
assert (blocking && connected) ^ !blocking; |
|
return connected; |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} catch (IOException ioe) { |
|
|
|
close(); |
|
throw SocketExceptions.of(ioe, remoteAddress); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean tryClose() throws IOException { |
|
assert Thread.holdsLock(stateLock) && state == ST_CLOSING; |
|
if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) { |
|
state = ST_CLOSED; |
|
nd.close(fd); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void tryFinishClose() { |
|
try { |
|
tryClose(); |
|
} catch (IOException ignore) { } |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void implCloseBlockingMode() throws IOException { |
|
synchronized (stateLock) { |
|
assert state < ST_CLOSING; |
|
state = ST_CLOSING; |
|
if (!tryClose()) { |
|
long reader = readerThread; |
|
long writer = writerThread; |
|
if (reader != 0 || writer != 0) { |
|
nd.preClose(fd); |
|
if (reader != 0) |
|
NativeThread.signal(reader); |
|
if (writer != 0) |
|
NativeThread.signal(writer); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void implCloseNonBlockingMode() throws IOException { |
|
boolean connected; |
|
synchronized (stateLock) { |
|
assert state < ST_CLOSING; |
|
connected = (state == ST_CONNECTED); |
|
state = ST_CLOSING; |
|
} |
|
|
|
|
|
readLock.lock(); |
|
readLock.unlock(); |
|
writeLock.lock(); |
|
writeLock.unlock(); |
|
|
|
// if the socket cannot be closed because it's registered with a Selector |
|
|
|
synchronized (stateLock) { |
|
if (state == ST_CLOSING && !tryClose() && connected && isRegistered()) { |
|
try { |
|
SocketOption<Integer> opt = StandardSocketOptions.SO_LINGER; |
|
int interval = (int) Net.getSocketOption(fd, Net.UNSPEC, opt); |
|
if (interval != 0) { |
|
if (interval > 0) { |
|
|
|
Net.setSocketOption(fd, Net.UNSPEC, opt, -1); |
|
} |
|
Net.shutdown(fd, Net.SHUT_WR); |
|
} |
|
} catch (IOException ignore) { } |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected void implCloseSelectableChannel() throws IOException { |
|
assert !isOpen(); |
|
if (isBlocking()) { |
|
implCloseBlockingMode(); |
|
} else { |
|
implCloseNonBlockingMode(); |
|
} |
|
} |
|
|
|
@Override |
|
public void kill() { |
|
synchronized (stateLock) { |
|
if (state == ST_CLOSING) { |
|
tryFinishClose(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public SocketChannel shutdownInput() throws IOException { |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (!isConnected()) |
|
throw new NotYetConnectedException(); |
|
if (!isInputClosed) { |
|
Net.shutdown(fd, Net.SHUT_RD); |
|
long thread = readerThread; |
|
if (thread != 0) |
|
NativeThread.signal(thread); |
|
isInputClosed = true; |
|
} |
|
return this; |
|
} |
|
} |
|
|
|
@Override |
|
public SocketChannel shutdownOutput() throws IOException { |
|
synchronized (stateLock) { |
|
ensureOpen(); |
|
if (!isConnected()) |
|
throw new NotYetConnectedException(); |
|
if (!isOutputClosed) { |
|
Net.shutdown(fd, Net.SHUT_WR); |
|
long thread = writerThread; |
|
if (thread != 0) |
|
NativeThread.signal(thread); |
|
isOutputClosed = true; |
|
} |
|
return this; |
|
} |
|
} |
|
|
|
boolean isInputOpen() { |
|
return !isInputClosed; |
|
} |
|
|
|
boolean isOutputOpen() { |
|
return !isOutputClosed; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean finishTimedConnect(long nanos) throws IOException { |
|
long startNanos = System.nanoTime(); |
|
boolean polled = Net.pollConnectNow(fd); |
|
while (!polled && isOpen()) { |
|
long remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
if (remainingNanos <= 0) { |
|
throw new SocketTimeoutException("Connect timed out"); |
|
} |
|
park(Net.POLLOUT, remainingNanos); |
|
polled = Net.pollConnectNow(fd); |
|
} |
|
return polled && isOpen(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void blockingConnect(SocketAddress remote, long nanos) throws IOException { |
|
SocketAddress sa = checkRemote(remote); |
|
try { |
|
readLock.lock(); |
|
try { |
|
writeLock.lock(); |
|
try { |
|
if (!isBlocking()) |
|
throw new IllegalBlockingModeException(); |
|
boolean connected = false; |
|
try { |
|
beginConnect(true, sa); |
|
|
|
lockedConfigureBlocking(false); |
|
try { |
|
int n; |
|
if (isUnixSocket()) { |
|
n = UnixDomainSockets.connect(fd, sa); |
|
} else { |
|
n = Net.connect(family, fd, sa); |
|
} |
|
connected = (n > 0) ? true : finishTimedConnect(nanos); |
|
} finally { |
|
|
|
tryLockedConfigureBlocking(true); |
|
} |
|
} finally { |
|
endConnect(true, connected); |
|
} |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} catch (IOException ioe) { |
|
|
|
close(); |
|
throw SocketExceptions.of(ioe, sa); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private int tryRead(byte[] b, int off, int len) throws IOException { |
|
ByteBuffer dst = Util.getTemporaryDirectBuffer(len); |
|
assert dst.position() == 0; |
|
try { |
|
int n = nd.read(fd, ((DirectBuffer)dst).address(), len); |
|
if (n > 0) { |
|
dst.get(b, off, n); |
|
} |
|
return n; |
|
} finally{ |
|
Util.offerFirstTemporaryDirectBuffer(dst); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private int timedRead(byte[] b, int off, int len, long nanos) throws IOException { |
|
long startNanos = System.nanoTime(); |
|
int n = tryRead(b, off, len); |
|
while (n == IOStatus.UNAVAILABLE && isOpen()) { |
|
long remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
if (remainingNanos <= 0) { |
|
throw new SocketTimeoutException("Read timed out"); |
|
} |
|
park(Net.POLLIN, remainingNanos); |
|
n = tryRead(b, off, len); |
|
} |
|
return n; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int blockingRead(byte[] b, int off, int len, long nanos) throws IOException { |
|
Objects.checkFromIndexSize(off, len, b.length); |
|
if (len == 0) { |
|
|
|
return 0; |
|
} |
|
|
|
readLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
|
|
|
|
if (!isBlocking()) |
|
throw new IllegalBlockingModeException(); |
|
|
|
int n = 0; |
|
try { |
|
beginRead(true); |
|
|
|
|
|
if (connectionReset) |
|
throwConnectionReset(); |
|
|
|
|
|
if (isInputClosed) |
|
return IOStatus.EOF; |
|
|
|
if (nanos > 0) { |
|
|
|
lockedConfigureBlocking(false); |
|
try { |
|
n = timedRead(b, off, len, nanos); |
|
} finally { |
|
|
|
tryLockedConfigureBlocking(true); |
|
} |
|
} else { |
|
|
|
n = tryRead(b, off, len); |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLIN); |
|
n = tryRead(b, off, len); |
|
} |
|
} |
|
} catch (ConnectionResetException e) { |
|
connectionReset = true; |
|
throwConnectionReset(); |
|
} finally { |
|
endRead(true, n > 0); |
|
if (n <= 0 && isInputClosed) |
|
return IOStatus.EOF; |
|
} |
|
assert n > 0 || n == -1; |
|
return n; |
|
} finally { |
|
readLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private int tryWrite(byte[] b, int off, int len) throws IOException { |
|
ByteBuffer src = Util.getTemporaryDirectBuffer(len); |
|
assert src.position() == 0; |
|
try { |
|
src.put(b, off, len); |
|
return nd.write(fd, ((DirectBuffer)src).address(), len); |
|
} finally { |
|
Util.offerFirstTemporaryDirectBuffer(src); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void blockingWriteFully(byte[] b, int off, int len) throws IOException { |
|
Objects.checkFromIndexSize(off, len, b.length); |
|
if (len == 0) { |
|
|
|
return; |
|
} |
|
|
|
writeLock.lock(); |
|
try { |
|
ensureOpenAndConnected(); |
|
|
|
|
|
if (!isBlocking()) |
|
throw new IllegalBlockingModeException(); |
|
|
|
|
|
int pos = off; |
|
int end = off + len; |
|
try { |
|
beginWrite(true); |
|
while (pos < end && isOpen()) { |
|
int size = end - pos; |
|
int n = tryWrite(b, pos, size); |
|
while (IOStatus.okayToRetry(n) && isOpen()) { |
|
park(Net.POLLOUT); |
|
n = tryWrite(b, pos, size); |
|
} |
|
if (n > 0) { |
|
pos += n; |
|
} |
|
} |
|
} finally { |
|
endWrite(true, pos >= end); |
|
} |
|
} finally { |
|
writeLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
int available() throws IOException { |
|
synchronized (stateLock) { |
|
ensureOpenAndConnected(); |
|
if (isInputClosed) { |
|
return 0; |
|
} else { |
|
return Net.available(fd); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) { |
|
int intOps = ski.nioInterestOps(); |
|
int oldOps = ski.nioReadyOps(); |
|
int newOps = initialOps; |
|
|
|
if ((ops & Net.POLLNVAL) != 0) { |
|
// This should only happen if this channel is pre-closed while a |
|
// selection operation is in progress |
|
|
|
return false; |
|
} |
|
|
|
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { |
|
newOps = intOps; |
|
ski.nioReadyOps(newOps); |
|
return (newOps & ~oldOps) != 0; |
|
} |
|
|
|
boolean connected = isConnected(); |
|
if (((ops & Net.POLLIN) != 0) && |
|
((intOps & SelectionKey.OP_READ) != 0) && connected) |
|
newOps |= SelectionKey.OP_READ; |
|
|
|
if (((ops & Net.POLLCONN) != 0) && |
|
((intOps & SelectionKey.OP_CONNECT) != 0) && isConnectionPending()) |
|
newOps |= SelectionKey.OP_CONNECT; |
|
|
|
if (((ops & Net.POLLOUT) != 0) && |
|
((intOps & SelectionKey.OP_WRITE) != 0) && connected) |
|
newOps |= SelectionKey.OP_WRITE; |
|
|
|
ski.nioReadyOps(newOps); |
|
return (newOps & ~oldOps) != 0; |
|
} |
|
|
|
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { |
|
return translateReadyOps(ops, ski.nioReadyOps(), ski); |
|
} |
|
|
|
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { |
|
return translateReadyOps(ops, 0, ski); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int translateInterestOps(int ops) { |
|
int newOps = 0; |
|
if ((ops & SelectionKey.OP_READ) != 0) |
|
newOps |= Net.POLLIN; |
|
if ((ops & SelectionKey.OP_WRITE) != 0) |
|
newOps |= Net.POLLOUT; |
|
if ((ops & SelectionKey.OP_CONNECT) != 0) |
|
newOps |= Net.POLLCONN; |
|
return newOps; |
|
} |
|
|
|
public FileDescriptor getFD() { |
|
return fd; |
|
} |
|
|
|
public int getFDVal() { |
|
return fdVal; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
StringBuilder sb = new StringBuilder(); |
|
sb.append(this.getClass().getSuperclass().getName()); |
|
sb.append('['); |
|
if (!isOpen()) |
|
sb.append("closed"); |
|
else { |
|
synchronized (stateLock) { |
|
switch (state) { |
|
case ST_UNCONNECTED: |
|
sb.append("unconnected"); |
|
break; |
|
case ST_CONNECTIONPENDING: |
|
sb.append("connection-pending"); |
|
break; |
|
case ST_CONNECTED: |
|
sb.append("connected"); |
|
if (isInputClosed) |
|
sb.append(" ishut"); |
|
if (isOutputClosed) |
|
sb.append(" oshut"); |
|
break; |
|
} |
|
SocketAddress addr = localAddress(); |
|
if (addr != null) { |
|
sb.append(" local="); |
|
if (isUnixSocket()) { |
|
sb.append(UnixDomainSockets.getRevealedLocalAddressAsString(addr)); |
|
} else { |
|
sb.append(Net.getRevealedLocalAddressAsString(addr)); |
|
} |
|
} |
|
if (remoteAddress() != null) { |
|
sb.append(" remote="); |
|
sb.append(remoteAddress().toString()); |
|
} |
|
} |
|
} |
|
sb.append(']'); |
|
return sb.toString(); |
|
} |
|
} |