8232673: (dc) DatagramChannel socket adaptor issues

Reviewed-by: dfuchs, chegar
This commit is contained in:
Alan Bateman 2019-11-02 10:02:18 +00:00
parent 5dafc279a7
commit db4909bf99
8 changed files with 948 additions and 245 deletions

View file

@ -606,7 +606,6 @@ class DatagramSocket implements java.io.Closeable {
* @see #bind(SocketAddress) * @see #bind(SocketAddress)
* @since 1.4 * @since 1.4
*/ */
public SocketAddress getLocalSocketAddress() { public SocketAddress getLocalSocketAddress() {
if (isClosed()) if (isClosed())
return null; return null;
@ -853,7 +852,7 @@ class DatagramSocket implements java.io.Closeable {
public InetAddress getLocalAddress() { public InetAddress getLocalAddress() {
if (isClosed()) if (isClosed())
return null; return null;
InetAddress in = null; InetAddress in;
try { try {
in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
if (in.isAnyLocalAddress()) { if (in.isAnyLocalAddress()) {
@ -874,8 +873,8 @@ class DatagramSocket implements java.io.Closeable {
* is bound. * is bound.
* *
* @return the port number on the local host to which this socket is bound, * @return the port number on the local host to which this socket is bound,
{@code -1} if the socket is closed, or * {@code -1} if the socket is closed, or
{@code 0} if it is not bound yet. * {@code 0} if it is not bound yet.
*/ */
public int getLocalPort() { public int getLocalPort() {
if (isClosed()) if (isClosed())
@ -887,15 +886,16 @@ class DatagramSocket implements java.io.Closeable {
} }
} }
/** Enable/disable SO_TIMEOUT with the specified timeout, in /**
* milliseconds. With this option set to a positive timeout value, * Enable/disable SO_TIMEOUT with the specified timeout, in
* a call to receive() for this DatagramSocket * milliseconds. With this option set to a positive timeout value,
* will block for only this amount of time. If the timeout expires, * a call to receive() for this DatagramSocket
* a <B>java.net.SocketTimeoutException</B> is raised, though the * will block for only this amount of time. If the timeout expires,
* DatagramSocket is still valid. A timeout of zero is interpreted * a <B>java.net.SocketTimeoutException</B> is raised, though the
* as an infinite timeout. * DatagramSocket is still valid. A timeout of zero is interpreted
* The option <B>must</B> be enabled prior to entering the blocking * as an infinite timeout.
* operation to have effect. * The option <B>must</B> be enabled prior to entering the blocking
* operation to have effect.
* *
* @param timeout the specified timeout in milliseconds. * @param timeout the specified timeout in milliseconds.
* @throws SocketException if there is an error in the underlying protocol, such as an UDP error. * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
@ -963,8 +963,7 @@ class DatagramSocket implements java.io.Closeable {
* negative. * negative.
* @see #getSendBufferSize() * @see #getSendBufferSize()
*/ */
public synchronized void setSendBufferSize(int size) public synchronized void setSendBufferSize(int size) throws SocketException {
throws SocketException{
if (!(size > 0)) { if (!(size > 0)) {
throw new IllegalArgumentException("negative send size"); throw new IllegalArgumentException("negative send size");
} }
@ -1021,8 +1020,7 @@ class DatagramSocket implements java.io.Closeable {
* negative. * negative.
* @see #getReceiveBufferSize() * @see #getReceiveBufferSize()
*/ */
public synchronized void setReceiveBufferSize(int size) public synchronized void setReceiveBufferSize(int size) throws SocketException {
throws SocketException{
if (size <= 0) { if (size <= 0) {
throw new IllegalArgumentException("invalid receive size"); throw new IllegalArgumentException("invalid receive size");
} }
@ -1039,8 +1037,7 @@ class DatagramSocket implements java.io.Closeable {
* @throws SocketException if there is an error in the underlying protocol, such as an UDP error. * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
* @see #setReceiveBufferSize(int) * @see #setReceiveBufferSize(int)
*/ */
public synchronized int getReceiveBufferSize() public synchronized int getReceiveBufferSize() throws SocketException {
throws SocketException{
if (isClosed()) if (isClosed())
throw new SocketException("Socket is closed"); throw new SocketException("Socket is closed");
int result = 0; int result = 0;

View file

@ -28,6 +28,8 @@ package sun.nio.ch;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner.Cleanable; import java.lang.ref.Cleaner.Cleanable;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.Inet4Address; import java.net.Inet4Address;
@ -39,6 +41,7 @@ import java.net.PortUnreachableException;
import java.net.ProtocolFamily; import java.net.ProtocolFamily;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketOption; import java.net.SocketOption;
import java.net.SocketTimeoutException;
import java.net.StandardProtocolFamily; import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions; import java.net.StandardSocketOptions;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -47,6 +50,7 @@ import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel; import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.MembershipKey; import java.nio.channels.MembershipKey;
import java.nio.channels.NotYetConnectedException; import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
@ -113,8 +117,17 @@ class DatagramChannelImpl
private InetSocketAddress localAddress; private InetSocketAddress localAddress;
private InetSocketAddress remoteAddress; private InetSocketAddress remoteAddress;
// Our socket adaptor, if any // Socket adaptor, created lazily
private DatagramSocket socket; private static final VarHandle SOCKET;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
SOCKET = l.findVarHandle(DatagramChannelImpl.class, "socket", DatagramSocket.class);
} catch (Exception e) {
throw new InternalError(e);
}
}
private volatile DatagramSocket socket;
// Multicast support // Multicast support
private MembershipRegistry registry; private MembershipRegistry registry;
@ -199,11 +212,14 @@ class DatagramChannelImpl
@Override @Override
public DatagramSocket socket() { public DatagramSocket socket() {
synchronized (stateLock) { DatagramSocket socket = this.socket;
if (socket == null) if (socket == null) {
socket = DatagramSocketAdaptor.create(this); socket = DatagramSocketAdaptor.create(this);
return socket; if (!SOCKET.compareAndSet(this, null, socket)) {
socket = this.socket;
}
} }
return socket;
} }
@Override @Override
@ -408,62 +424,35 @@ class DatagramChannelImpl
public SocketAddress receive(ByteBuffer dst) throws IOException { public SocketAddress receive(ByteBuffer dst) throws IOException {
if (dst.isReadOnly()) if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer"); throw new IllegalArgumentException("Read-only buffer");
readLock.lock(); readLock.lock();
try { try {
boolean blocking = isBlocking(); boolean blocking = isBlocking();
boolean completed = false;
int n = 0; int n = 0;
ByteBuffer bb = null;
try { try {
SocketAddress remote = beginRead(blocking, false); SocketAddress remote = beginRead(blocking, false);
boolean connected = (remote != null); boolean connected = (remote != null);
SecurityManager sm = System.getSecurityManager(); SecurityManager sm = System.getSecurityManager();
if (connected || (sm == null)) { if (connected || (sm == null)) {
// connected or no security manager // connected or no security manager
n = receive(fd, dst, connected); n = receive(dst, connected);
if (blocking) { if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) { while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN); park(Net.POLLIN);
n = receive(fd, dst, connected); n = receive(dst, connected);
} }
} else if (n == IOStatus.UNAVAILABLE) {
return null;
} }
} else { } else {
// Cannot receive into user's buffer when running with a // security manager and unconnected
// security manager and not connected n = untrustedReceive(dst);
bb = Util.getTemporaryDirectBuffer(dst.remaining());
for (;;) {
n = receive(fd, bb, connected);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN);
n = receive(fd, bb, connected);
}
} else if (n == IOStatus.UNAVAILABLE) {
return null;
}
InetSocketAddress isa = (InetSocketAddress)sender;
try {
sm.checkAccept(isa.getAddress().getHostAddress(),
isa.getPort());
} catch (SecurityException se) {
// Ignore packet
bb.clear();
n = 0;
continue;
}
bb.flip();
dst.put(bb);
break;
}
} }
assert sender != null; if (n == IOStatus.UNAVAILABLE)
return null;
completed = (n > 0) || (n == 0 && isOpen());
return sender; return sender;
} finally { } finally {
if (bb != null) endRead(blocking, completed);
Util.releaseTemporaryDirectBuffer(bb);
endRead(blocking, n > 0);
assert IOStatus.check(n); assert IOStatus.check(n);
} }
} finally { } finally {
@ -471,15 +460,164 @@ class DatagramChannelImpl
} }
} }
private int receive(FileDescriptor fd, ByteBuffer dst, boolean connected) /**
* Receives a datagram into an untrusted buffer. When there is a security
* manager set, and the socket is not connected, datagrams have to be received
* into a buffer that is not accessible to the user. The datagram is copied
* into the user's buffer when the sender address is accepted by the security
* manager.
*
* @return the size of the datagram or IOStatus.UNAVAILABLE
*/
private int untrustedReceive(ByteBuffer dst) throws IOException {
SecurityManager sm = System.getSecurityManager();
assert readLock.isHeldByCurrentThread()
&& sm != null && remoteAddress == null;
ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
try {
boolean blocking = isBlocking();
for (;;) {
int n = receive(bb, false);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN);
n = receive(bb, false);
}
} else if (n == IOStatus.UNAVAILABLE) {
return n;
}
InetSocketAddress isa = (InetSocketAddress) sender;
try {
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
bb.flip();
dst.put(bb);
return n;
} catch (SecurityException se) {
// ignore datagram
bb.clear();
}
}
} finally {
Util.releaseTemporaryDirectBuffer(bb);
}
}
/**
* Receives a datagram into the given buffer.
*
* @apiNote This method is for use by the socket adaptor. The buffer is
* assumed to be trusted, meaning it is not accessible to user code.
*
* @throws IllegalBlockingModeException if the channel is non-blocking
* @throws SocketTimeoutException if the timeout elapses
*/
SocketAddress blockingReceive(ByteBuffer dst, long nanos) throws IOException {
readLock.lock();
try {
ensureOpen();
if (!isBlocking())
throw new IllegalBlockingModeException();
SecurityManager sm = System.getSecurityManager();
boolean connected = isConnected();
SocketAddress sender;
do {
if (nanos > 0) {
sender = trustedBlockingReceive(dst, nanos);
} else {
sender = trustedBlockingReceive(dst);
}
// check sender when security manager set and not connected
if (sm != null && !connected) {
InetSocketAddress isa = (InetSocketAddress) sender;
try {
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
} catch (SecurityException e) {
sender = null;
}
}
} while (sender == null);
return sender;
} finally {
readLock.unlock();
}
}
/**
* Receives a datagram into given buffer. This method is used to support
* the socket adaptor. The buffer is assumed to be trusted.
* @throws SocketTimeoutException if the timeout elapses
*/
private SocketAddress trustedBlockingReceive(ByteBuffer dst)
throws IOException throws IOException
{ {
assert readLock.isHeldByCurrentThread() && isBlocking();
boolean completed = false;
int n = 0;
try {
SocketAddress remote = beginRead(true, false);
boolean connected = (remote != null);
n = receive(dst, connected);
while (n == IOStatus.UNAVAILABLE && isOpen()) {
park(Net.POLLIN);
n = receive(dst, connected);
}
completed = (n > 0) || (n == 0 && isOpen());
return sender;
} finally {
endRead(true, completed);
assert IOStatus.check(n);
}
}
/**
* Receives a datagram into given buffer with a timeout. This method is
* used to support the socket adaptor. The buffer is assumed to be trusted.
* @throws SocketTimeoutException if the timeout elapses
*/
private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos)
throws IOException
{
assert readLock.isHeldByCurrentThread() && isBlocking();
boolean completed = false;
int n = 0;
try {
SocketAddress remote = beginRead(true, false);
boolean connected = (remote != null);
// change socket to non-blocking
lockedConfigureBlocking(false);
try {
long startNanos = System.nanoTime();
n = receive(dst, connected);
while (n == IOStatus.UNAVAILABLE && isOpen()) {
long remainingNanos = nanos - (System.nanoTime() - startNanos);
if (remainingNanos <= 0) {
throw new SocketTimeoutException("Receive timed out");
}
park(Net.POLLIN, remainingNanos);
n = receive(dst, connected);
}
completed = (n > 0) || (n == 0 && isOpen());
return sender;
} finally {
// restore socket to blocking mode (if channel is open)
tryLockedConfigureBlocking(true);
}
} finally {
endRead(true, completed);
assert IOStatus.check(n);
}
}
private int receive(ByteBuffer dst, boolean connected) throws IOException {
int pos = dst.position(); int pos = dst.position();
int lim = dst.limit(); int lim = dst.limit();
assert (pos <= lim); assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0); int rem = (pos <= lim ? lim - pos : 0);
if (dst instanceof DirectBuffer && rem > 0) if (dst instanceof DirectBuffer && rem > 0)
return receiveIntoNativeBuffer(fd, dst, rem, pos, connected); return receiveIntoNativeBuffer(dst, rem, pos, connected);
// Substitute a native buffer. If the supplied buffer is empty // Substitute a native buffer. If the supplied buffer is empty
// we must instead use a nonempty buffer, otherwise the call // we must instead use a nonempty buffer, otherwise the call
@ -487,7 +625,7 @@ class DatagramChannelImpl
int newSize = Math.max(rem, 1); int newSize = Math.max(rem, 1);
ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize); ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
try { try {
int n = receiveIntoNativeBuffer(fd, bb, newSize, 0, connected); int n = receiveIntoNativeBuffer(bb, newSize, 0, connected);
bb.flip(); bb.flip();
if (n > 0 && rem > 0) if (n > 0 && rem > 0)
dst.put(bb); dst.put(bb);
@ -497,8 +635,8 @@ class DatagramChannelImpl
} }
} }
private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb, private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos,
int rem, int pos, boolean connected) boolean connected)
throws IOException throws IOException
{ {
int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected); int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected);
@ -563,6 +701,25 @@ class DatagramChannelImpl
} }
} }
/**
* Sends a datagram from the bytes in given buffer.
*
* @apiNote This method is for use by the socket adaptor.
*
* @throws IllegalBlockingModeException if the channel is non-blocking
*/
void blockingSend(ByteBuffer src, SocketAddress target) throws IOException {
writeLock.lock();
try {
ensureOpen();
if (!isBlocking())
throw new IllegalBlockingModeException();
send(src, target);
} finally {
writeLock.unlock();
}
}
private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target) private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target)
throws IOException throws IOException
{ {
@ -785,10 +942,7 @@ class DatagramChannelImpl
try { try {
writeLock.lock(); writeLock.lock();
try { try {
synchronized (stateLock) { lockedConfigureBlocking(block);
ensureOpen();
IOUtil.configureBlocking(fd, block);
}
} finally { } finally {
writeLock.unlock(); writeLock.unlock();
} }
@ -797,6 +951,36 @@ class DatagramChannelImpl
} }
} }
/**
* Adjusts the blocking mode. readLock or writeLock must already be held.
*/
private void lockedConfigureBlocking(boolean block) throws IOException {
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
synchronized (stateLock) {
ensureOpen();
IOUtil.configureBlocking(fd, block);
}
}
/**
* Adjusts the blocking mode if the channel is open. readLock or writeLock
* must already be held.
*
* @return {@code true} if the blocking mode was adjusted, {@code false} if
* the blocking mode was not adjusted because the channel is closed
*/
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;
}
}
}
InetSocketAddress localAddress() { InetSocketAddress localAddress() {
synchronized (stateLock) { synchronized (stateLock) {
return localAddress; return localAddress;
@ -861,6 +1045,16 @@ class DatagramChannelImpl
@Override @Override
public DatagramChannel connect(SocketAddress sa) throws IOException { public DatagramChannel connect(SocketAddress sa) throws IOException {
return connect(sa, true);
}
/**
* Connects the channel's socket.
*
* @param sa the remote address to which this channel is to be connected
* @param check true to check if the channel is already connected.
*/
DatagramChannel connect(SocketAddress sa, boolean check) throws IOException {
InetSocketAddress isa = Net.checkAddress(sa, family); InetSocketAddress isa = Net.checkAddress(sa, family);
SecurityManager sm = System.getSecurityManager(); SecurityManager sm = System.getSecurityManager();
if (sm != null) { if (sm != null) {
@ -879,7 +1073,7 @@ class DatagramChannelImpl
try { try {
synchronized (stateLock) { synchronized (stateLock) {
ensureOpen(); ensureOpen();
if (state == ST_CONNECTED) if (check && state == ST_CONNECTED)
throw new AlreadyConnectedException(); throw new AlreadyConnectedException();
// ensure that the socket is bound // ensure that the socket is bound
@ -908,7 +1102,7 @@ class DatagramChannelImpl
} }
try { try {
ByteBuffer buf = ByteBuffer.allocate(100); ByteBuffer buf = ByteBuffer.allocate(100);
while (receive(fd, buf, false) > 0) { while (receive(buf, false) >= 0) {
buf.clear(); buf.clear();
} }
} finally { } finally {
@ -1331,30 +1525,6 @@ class DatagramChannelImpl
return translateReadyOps(ops, 0, ski); return translateReadyOps(ops, 0, ski);
} }
/**
* Poll this channel's socket for reading up to the given timeout.
* @return {@code true} if the socket is polled
*/
boolean pollRead(long timeout) throws IOException {
boolean blocking = isBlocking();
assert Thread.holdsLock(blockingLock()) && blocking;
readLock.lock();
try {
boolean polled = false;
try {
beginRead(blocking, false);
int events = Net.poll(fd, Net.POLLIN, timeout);
polled = (events != 0);
} finally {
endRead(blocking, polled);
}
return polled;
} finally {
readLock.unlock();
}
}
/** /**
* Translates an interest operation set into a native poll event set * Translates an interest operation set into a native poll event set
*/ */

View file

@ -26,6 +26,9 @@
package sun.nio.ch; package sun.nio.ch;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.VarHandle;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.DatagramSocketImpl; import java.net.DatagramSocketImpl;
@ -35,15 +38,16 @@ import java.net.NetworkInterface;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketException; import java.net.SocketException;
import java.net.SocketOption; import java.net.SocketOption;
import java.net.SocketTimeoutException;
import java.net.StandardSocketOptions; import java.net.StandardSocketOptions;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel; import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException; import java.security.AccessController;
import java.util.Objects; import java.security.PrivilegedAction;
import java.util.Set; import java.util.Set;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
// Make a datagram-socket channel look like a datagram socket. // Make a datagram-socket channel look like a datagram socket.
// //
@ -61,13 +65,9 @@ class DatagramSocketAdaptor
// Timeout "option" value for receives // Timeout "option" value for receives
private volatile int timeout; private volatile int timeout;
// ## super will create a useless impl // create DatagramSocket with useless impl
private DatagramSocketAdaptor(DatagramChannelImpl dc) { private DatagramSocketAdaptor(DatagramChannelImpl dc) {
// Invoke the DatagramSocketAdaptor(SocketAddress) constructor, super(new DummyDatagramSocketImpl());
// passing a dummy DatagramSocketImpl object to avoid any native
// resource allocation in super class and invoking our bind method
// before the dc field is initialized.
super(dummyDatagramSocket);
this.dc = dc; this.dc = dc;
} }
@ -75,17 +75,9 @@ class DatagramSocketAdaptor
return new DatagramSocketAdaptor(dc); return new DatagramSocketAdaptor(dc);
} }
private void connectInternal(SocketAddress remote) private void connectInternal(SocketAddress remote) throws SocketException {
throws SocketException
{
InetSocketAddress isa = Net.asInetSocketAddress(remote);
int port = isa.getPort();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException("connect: " + port);
if (remote == null)
throw new IllegalArgumentException("connect: null address");
try { try {
dc.connect(remote); dc.connect(remote, false); // skips check for already connected
} catch (ClosedChannelException e) { } catch (ClosedChannelException e) {
// ignore // ignore
} catch (Exception x) { } catch (Exception x) {
@ -95,9 +87,12 @@ class DatagramSocketAdaptor
@Override @Override
public void bind(SocketAddress local) throws SocketException { public void bind(SocketAddress local) throws SocketException {
if (local != null) {
local = Net.asInetSocketAddress(local);
} else {
local = new InetSocketAddress(0);
}
try { try {
if (local == null)
local = new InetSocketAddress(0);
dc.bind(local); dc.bind(local);
} catch (Exception x) { } catch (Exception x) {
Net.translateToSocketException(x); Net.translateToSocketException(x);
@ -106,17 +101,20 @@ class DatagramSocketAdaptor
@Override @Override
public void connect(InetAddress address, int port) { public void connect(InetAddress address, int port) {
if (address == null)
throw new IllegalArgumentException("Address can't be null");
try { try {
connectInternal(new InetSocketAddress(address, port)); connectInternal(new InetSocketAddress(address, port));
} catch (SocketException x) { } catch (SocketException x) {
// Yes, j.n.DatagramSocket really does this throw new Error(x);
} }
} }
@Override @Override
public void connect(SocketAddress remote) throws SocketException { public void connect(SocketAddress remote) throws SocketException {
Objects.requireNonNull(remote, "Address can't be null"); if (remote == null)
connectInternal(remote); throw new IllegalArgumentException("Address can't be null");
connectInternal(Net.asInetSocketAddress(remote));
} }
@Override @Override
@ -157,80 +155,84 @@ class DatagramSocketAdaptor
@Override @Override
public SocketAddress getLocalSocketAddress() { public SocketAddress getLocalSocketAddress() {
return dc.localAddress(); try {
return dc.getLocalAddress();
} catch (ClosedChannelException e) {
return null;
} catch (Exception x) {
throw new Error(x);
}
} }
@Override @Override
public void send(DatagramPacket p) throws IOException { public void send(DatagramPacket p) throws IOException {
synchronized (dc.blockingLock()) { ByteBuffer bb = null;
if (!dc.isBlocking()) try {
throw new IllegalBlockingModeException(); InetSocketAddress target;
try { synchronized (p) {
synchronized (p) { // copy bytes to temporary direct buffer
ByteBuffer bb = ByteBuffer.wrap(p.getData(), int len = p.getLength();
p.getOffset(), bb = Util.getTemporaryDirectBuffer(len);
p.getLength()); bb.put(p.getData(), p.getOffset(), len);
if (dc.isConnected()) { bb.flip();
if (p.getAddress() == null) {
// Legacy DatagramSocket will send in this case // target address
// and set address and port of the packet if (p.getAddress() == null) {
InetSocketAddress isa = dc.remoteAddress(); InetSocketAddress remote = dc.remoteAddress();
p.setPort(isa.getPort()); if (remote == null) {
p.setAddress(isa.getAddress()); // not specified by DatagramSocket
dc.write(bb); throw new IllegalArgumentException("Address not set");
} else {
// Target address may not match connected address
dc.send(bb, p.getSocketAddress());
}
} else {
// Not connected so address must be valid or throw
dc.send(bb, p.getSocketAddress());
} }
// set address/port to maintain compatibility with DatagramSocket
p.setAddress(remote.getAddress());
p.setPort(remote.getPort());
target = remote;
} else {
// throws IllegalArgumentException if port not set
target = (InetSocketAddress) p.getSocketAddress();
} }
} catch (IOException x) {
Net.translateException(x);
} }
} // send datagram
} try {
dc.blockingSend(bb, target);
private SocketAddress receive(ByteBuffer bb) throws IOException { } catch (AlreadyConnectedException e) {
assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking(); throw new IllegalArgumentException("Connected and packet address differ");
} catch (ClosedChannelException e) {
long to = this.timeout; var exc = new SocketException("Socket closed");
if (to == 0) { exc.initCause(e);
return dc.receive(bb); throw exc;
} else { }
for (;;) { } finally {
if (!dc.isOpen()) if (bb != null) {
throw new ClosedChannelException(); Util.offerFirstTemporaryDirectBuffer(bb);
long st = System.currentTimeMillis();
if (dc.pollRead(to)) {
return dc.receive(bb);
}
to -= System.currentTimeMillis() - st;
if (to <= 0)
throw new SocketTimeoutException();
} }
} }
} }
@Override @Override
public void receive(DatagramPacket p) throws IOException { public void receive(DatagramPacket p) throws IOException {
synchronized (dc.blockingLock()) { // get temporary direct buffer with a capacity of p.bufLength
if (!dc.isBlocking()) int bufLength = DatagramPackets.getBufLength(p);
throw new IllegalBlockingModeException(); ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength);
try { try {
synchronized (p) { long nanos = MILLISECONDS.toNanos(timeout);
ByteBuffer bb = ByteBuffer.wrap(p.getData(), SocketAddress sender = dc.blockingReceive(bb, nanos);
p.getOffset(), bb.flip();
p.getLength()); synchronized (p) {
SocketAddress sender = receive(bb); // copy bytes to the DatagramPacket and set length
p.setSocketAddress(sender); int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p));
p.setLength(bb.position() - p.getOffset()); bb.get(p.getData(), p.getOffset(), len);
} DatagramPackets.setLength(p, len);
} catch (IOException x) {
Net.translateException(x); // sender address
p.setSocketAddress(sender);
} }
} catch (ClosedChannelException e) {
var exc = new SocketException("Socket closed");
exc.initCause(e);
throw exc;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
} }
} }
@ -257,19 +259,16 @@ class DatagramSocketAdaptor
public int getLocalPort() { public int getLocalPort() {
if (isClosed()) if (isClosed())
return -1; return -1;
try { InetSocketAddress local = dc.localAddress();
InetSocketAddress local = dc.localAddress(); if (local != null) {
if (local != null) { return local.getPort();
return local.getPort();
}
} catch (Exception x) {
} }
return 0; return 0;
} }
@Override @Override
public void setSoTimeout(int timeout) throws SocketException { public void setSoTimeout(int timeout) throws SocketException {
if (!dc.isOpen()) if (isClosed())
throw new SocketException("Socket is closed"); throw new SocketException("Socket is closed");
if (timeout < 0) if (timeout < 0)
throw new IllegalArgumentException("timeout < 0"); throw new IllegalArgumentException("timeout < 0");
@ -278,7 +277,7 @@ class DatagramSocketAdaptor
@Override @Override
public int getSoTimeout() throws SocketException { public int getSoTimeout() throws SocketException {
if (!dc.isOpen()) if (isClosed())
throw new SocketException("Socket is closed"); throw new SocketException("Socket is closed");
return timeout; return timeout;
} }
@ -353,7 +352,6 @@ class DatagramSocketAdaptor
@Override @Override
public boolean getReuseAddress() throws SocketException { public boolean getReuseAddress() throws SocketException {
return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
} }
@Override @Override
@ -411,50 +409,157 @@ class DatagramSocketAdaptor
return dc.supportedOptions(); return dc.supportedOptions();
} }
/*
* A dummy implementation of DatagramSocketImpl that can be passed to the
* DatagramSocket constructor so that no native resources are allocated in
* super class.
*/
private static final DatagramSocketImpl dummyDatagramSocket
= new DatagramSocketImpl()
{
protected void create() throws SocketException {}
protected void bind(int lport, InetAddress laddr) throws SocketException {} /**
* DatagramSocketImpl implementation where all methods throw an error.
*/
private static class DummyDatagramSocketImpl extends DatagramSocketImpl {
private static <T> T shouldNotGetHere() {
throw new InternalError("Should not get here");
}
protected void send(DatagramPacket p) throws IOException {} @Override
protected void create() {
shouldNotGetHere();
}
protected int peek(InetAddress i) throws IOException { return 0; } @Override
protected void bind(int lport, InetAddress laddr) {
shouldNotGetHere();
}
protected int peekData(DatagramPacket p) throws IOException { return 0; } @Override
protected void send(DatagramPacket p) {
shouldNotGetHere();
}
protected void receive(DatagramPacket p) throws IOException {} @Override
protected int peek(InetAddress address) {
return shouldNotGetHere();
}
@Deprecated @Override
protected void setTTL(byte ttl) throws IOException {} protected int peekData(DatagramPacket p) {
return shouldNotGetHere();
}
@Deprecated @Override
protected byte getTTL() throws IOException { return 0; } protected void receive(DatagramPacket p) {
shouldNotGetHere();
}
protected void setTimeToLive(int ttl) throws IOException {} @Deprecated
protected void setTTL(byte ttl) {
shouldNotGetHere();
}
protected int getTimeToLive() throws IOException { return 0;} @Deprecated
protected byte getTTL() {
return shouldNotGetHere();
}
protected void join(InetAddress inetaddr) throws IOException {} @Override
protected void setTimeToLive(int ttl) {
shouldNotGetHere();
}
protected void leave(InetAddress inetaddr) throws IOException {} @Override
protected int getTimeToLive() {
return shouldNotGetHere();
}
protected void joinGroup(SocketAddress mcastaddr, @Override
NetworkInterface netIf) throws IOException {} protected void join(InetAddress group) {
shouldNotGetHere();
}
protected void leaveGroup(SocketAddress mcastaddr, @Override
NetworkInterface netIf) throws IOException {} protected void leave(InetAddress inetaddr) {
shouldNotGetHere();
}
protected void close() {} @Override
protected void joinGroup(SocketAddress group, NetworkInterface netIf) {
shouldNotGetHere();
}
public Object getOption(int optID) throws SocketException { return null;} @Override
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
shouldNotGetHere();
}
public void setOption(int optID, Object value) throws SocketException {} @Override
}; protected void close() {
} shouldNotGetHere();
}
@Override
public Object getOption(int optID) {
return shouldNotGetHere();
}
@Override
public void setOption(int optID, Object value) {
shouldNotGetHere();
}
@Override
protected <T> void setOption(SocketOption<T> name, T value) {
shouldNotGetHere();
}
@Override
protected <T> T getOption(SocketOption<T> name) {
return shouldNotGetHere();
}
@Override
protected Set<SocketOption<?>> supportedOptions() {
return shouldNotGetHere();
}
}
/**
* Defines static methods to get/set DatagramPacket fields and workaround
* DatagramPacket deficiencies.
*/
private static class DatagramPackets {
private static final VarHandle LENGTH;
private static final VarHandle BUF_LENGTH;
static {
try {
PrivilegedAction<Lookup> pa = () -> {
try {
return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup());
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
};
MethodHandles.Lookup l = AccessController.doPrivileged(pa);
LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class);
BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be
* used at this time because it sets both the length and bufLength fields.
*/
static void setLength(DatagramPacket p, int value) {
synchronized (p) {
LENGTH.set(p, value);
}
}
/**
* Returns the value of the DatagramPacket.bufLength field.
*/
static int getBufLength(DatagramPacket p) {
synchronized (p) {
return (int) BUF_LENGTH.get(p);
}
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,20 +22,22 @@
*/ */
/* @test /* @test
* @bug 4313882 4981129 8143610 * @bug 4313882 4981129 8143610 8232673
* @summary Unit test for datagram-socket-channel adaptors * @summary Unit test for datagram-socket-channel adaptors
* @modules java.base/java.net:+open
* @library .. /test/lib * @library .. /test/lib
* @build jdk.test.lib.Utils TestServers * @build jdk.test.lib.Utils TestServers
* @run main AdaptDatagramSocket * @run main AdaptorBasic
* @key randomness * @key randomness
*/ */
import java.net.*; import java.net.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.lang.reflect.Field;
public class AdaptDatagramSocket { public class AdaptorBasic {
static java.io.PrintStream out = System.out; static java.io.PrintStream out = System.out;
static Random rand = new Random(); static Random rand = new Random();
@ -46,13 +48,19 @@ public class AdaptDatagramSocket {
+ "]"); + "]");
} }
static void test(DatagramSocket ds, InetSocketAddress dst, static int getBufLength(DatagramPacket p) throws Exception {
boolean shouldTimeout) Field f = DatagramPacket.class.getDeclaredField("bufLength");
f.setAccessible(true);
return (int) f.get(p);
}
static void test(DatagramSocket ds, InetSocketAddress dst, boolean shouldTimeout)
throws Exception throws Exception
{ {
DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst); DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
rand.nextBytes(op.getData()); rand.nextBytes(op.getData());
DatagramPacket ip = new DatagramPacket(new byte[100], 19, 100 - 19); int bufLength = 100 - 19;
DatagramPacket ip = new DatagramPacket(new byte[100], 19, bufLength);
out.println("pre op: " + toString(op) + " ip: " + toString(ip)); out.println("pre op: " + toString(op) + " ip: " + toString(ip));
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
@ -61,10 +69,6 @@ public class AdaptDatagramSocket {
for (;;) { for (;;) {
try { try {
ds.receive(ip); ds.receive(ip);
if (ip.getLength() == 0) { // ## Not sure why this happens
ip.setLength(100 - 19);
continue;
}
} catch (SocketTimeoutException x) { } catch (SocketTimeoutException x) {
if (shouldTimeout) { if (shouldTimeout) {
out.println("Receive timed out, as expected"); out.println("Receive timed out, as expected");
@ -88,6 +92,10 @@ public class AdaptDatagramSocket {
throw new Exception("Incorrect sender address, expected: " + dst throw new Exception("Incorrect sender address, expected: " + dst
+ " actual: " + ip.getSocketAddress()); + " actual: " + ip.getSocketAddress());
} }
if (getBufLength(ip) != bufLength) {
throw new Exception("DatagramPacket bufLength changed by receive!!!");
}
} }
static void test(InetSocketAddress dst, static void test(InetSocketAddress dst,

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8232673
* @summary Test DatagramChannel socket adaptor with concurrent send/receive
*/
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class AdaptorConcurrentIO {
public static void main(String[] args) throws Exception {
testConcurrentSendReceive(0);
testConcurrentSendReceive(60_000);
}
/**
* Starts a task that blocks in the adaptor's receive method, then invokes
* the adaptor's send method to send a datagram. If the adaptor were using
* the channel's blockingLock then send without be blocked waiting for
* the receive to complete.
*/
static void testConcurrentSendReceive(int timeout) throws Exception {
try (DatagramChannel dc = DatagramChannel.open()) {
InetAddress lb = InetAddress.getLoopbackAddress();
dc.bind(new InetSocketAddress(lb, 0));
DatagramSocket s = dc.socket();
s.setSoTimeout(timeout);
ExecutorService pool = Executors.newSingleThreadExecutor();
try {
Future<String> result = pool.submit(() -> {
byte[] data = new byte[100];
DatagramPacket p = new DatagramPacket(data, 0, data.length);
s.receive(p);
return new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8");
});
Thread.sleep(200); // give chance for thread to block
byte[] data = "hello".getBytes("UTF-8");
DatagramPacket p = new DatagramPacket(data, 0, data.length);
p.setSocketAddress(s.getLocalSocketAddress());
s.send(p);
String msg = result.get();
if (!msg.equals("hello"))
throw new RuntimeException("Unexpected message: " + msg);
} finally {
pool.shutdown();
}
}
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8232673
* @summary Test DatagramChannel socket adaptor connect method with illegal args
* @run testng AdaptorConnect
*/
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.DatagramChannel;
import static java.net.InetAddress.getLoopbackAddress;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test
public class AdaptorConnect {
/**
* Invoke the given socket's connect method with illegal arguments.
*/
private void testConnectWithIllegalArguments(DatagramSocket s) {
assertThrows(IllegalArgumentException.class, () -> s.connect(null));
assertThrows(IllegalArgumentException.class, () -> s.connect(null, 7000));
assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), -1));
assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), 100_000));
SocketAddress sillyAddress = new SocketAddress() { };
assertThrows(IllegalArgumentException.class, () -> s.connect(sillyAddress));
SocketAddress unresolved = InetSocketAddress.createUnresolved("foo", 7777);
assertThrows(SocketException.class, () -> s.connect(unresolved));
}
/**
* Test connect method with an open socket.
*/
public void testOpenSocket() throws Exception {
try (DatagramChannel dc = DatagramChannel.open()) {
DatagramSocket s = dc.socket();
testConnectWithIllegalArguments(s);
// should not be bound or connected
assertTrue(s.getLocalSocketAddress() == null);
assertTrue(s.getRemoteSocketAddress() == null);
// connect(SocketAddress)
var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001);
s.connect(remote1);
assertEquals(s.getRemoteSocketAddress(), remote1);
testConnectWithIllegalArguments(s);
assertEquals(s.getRemoteSocketAddress(), remote1);
// connect(SocketAddress)
var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002);
s.connect(remote2);
assertEquals(s.getRemoteSocketAddress(), remote2);
testConnectWithIllegalArguments(s);
assertEquals(s.getRemoteSocketAddress(), remote2);
// connect(InetAddress, int)
var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003);
s.connect(remote3.getAddress(), remote3.getPort());
assertEquals(s.getRemoteSocketAddress(), remote3);
testConnectWithIllegalArguments(s);
assertEquals(s.getRemoteSocketAddress(), remote3);
// connect(InetAddress, int)
var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004);
s.connect(remote4.getAddress(), remote4.getPort());
assertEquals(s.getRemoteSocketAddress(), remote4);
testConnectWithIllegalArguments(s);
assertEquals(s.getRemoteSocketAddress(), remote4);
}
}
/**
* Test connect method with a closed socket.
*/
public void testClosedSocket() throws Exception {
DatagramChannel dc = DatagramChannel.open();
DatagramSocket s = dc.socket();
dc.close();
testConnectWithIllegalArguments(s);
// connect does not throw an exception when closed
var remote = new InetSocketAddress(getLoopbackAddress(), 7001);
s.connect(remote);
s.connect(remote.getAddress(), remote.getPort());
}
}

View file

@ -0,0 +1,223 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8232673
* @summary Test the DatagramChannel socket adaptor getter methods
* @run testng AdaptorGetters
*/
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test
public class AdaptorGetters {
/**
* Test getters on unbound socket, before and after it is closed.
*/
public void testUnboundSocket() throws Exception {
DatagramChannel dc = DatagramChannel.open();
DatagramSocket s = dc.socket();
try {
// state
assertFalse(s.isBound());
assertFalse(s.isConnected());
assertFalse(s.isClosed());
// local address
assertTrue(s.getLocalAddress().isAnyLocalAddress());
assertTrue(s.getLocalPort() == 0);
assertTrue(s.getLocalSocketAddress() == null);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
} finally {
dc.close();
}
// state
assertFalse(s.isBound());
assertFalse(s.isConnected());
assertTrue(s.isClosed());
// local address
assertTrue(s.getLocalAddress() == null);
assertTrue(s.getLocalPort() == -1);
assertTrue(s.getLocalSocketAddress() == null);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
assertTrue((s.getRemoteSocketAddress() == null));
}
/**
* Test getters on bound socket, before and after it is closed.
*/
public void testBoundSocket() throws Exception {
DatagramChannel dc = DatagramChannel.open();
DatagramSocket s = dc.socket();
try {
dc.bind(new InetSocketAddress(0));
var localAddress = (InetSocketAddress) dc.getLocalAddress();
// state
assertTrue(s.isBound());
assertFalse(s.isConnected());
assertFalse(s.isClosed());
// local address
assertEquals(s.getLocalAddress(), localAddress.getAddress());
assertTrue(s.getLocalPort() == localAddress.getPort());
assertEquals(s.getLocalSocketAddress(), localAddress);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
assertTrue((s.getRemoteSocketAddress() == null));
} finally {
dc.close();
}
// state
assertTrue(s.isBound());
assertFalse(s.isConnected());
assertTrue(s.isClosed());
// local address
assertTrue(s.getLocalAddress() == null);
assertTrue(s.getLocalPort() == -1);
assertTrue(s.getLocalSocketAddress() == null);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
assertTrue((s.getRemoteSocketAddress() == null));
}
/**
* Test getters on connected socket, before and after it is closed.
*/
public void testConnectedSocket() throws Exception {
var loopback = InetAddress.getLoopbackAddress();
var remoteAddress = new InetSocketAddress(loopback, 7777);
DatagramChannel dc = DatagramChannel.open();
DatagramSocket s = dc.socket();
try {
dc.connect(remoteAddress);
var localAddress = (InetSocketAddress) dc.getLocalAddress();
// state
assertTrue(s.isBound());
assertTrue(s.isConnected());
assertFalse(s.isClosed());
// local address
assertEquals(s.getLocalAddress(), localAddress.getAddress());
assertTrue(s.getLocalPort() == localAddress.getPort());
assertEquals(s.getLocalSocketAddress(), localAddress);
// remote address
assertEquals(s.getInetAddress(), remoteAddress.getAddress());
assertTrue(s.getPort() == remoteAddress.getPort());
assertEquals(s.getRemoteSocketAddress(), remoteAddress);
} finally {
dc.close();
}
// state
assertTrue(s.isBound());
assertTrue(s.isConnected());
assertTrue(s.isClosed());
// local address
assertTrue(s.getLocalAddress() == null);
assertTrue(s.getLocalPort() == -1);
assertTrue(s.getLocalSocketAddress() == null);
// remote address
assertEquals(s.getInetAddress(), remoteAddress.getAddress());
assertTrue(s.getPort() == remoteAddress.getPort());
assertEquals(s.getRemoteSocketAddress(), remoteAddress);
}
/**
* Test getters on disconnected socket, before and after it is closed.
*/
public void testDisconnectedSocket() throws Exception {
DatagramChannel dc = DatagramChannel.open();
DatagramSocket s = dc.socket();
try {
var loopback = InetAddress.getLoopbackAddress();
dc.connect(new InetSocketAddress(loopback, 7777));
dc.disconnect();
var localAddress = (InetSocketAddress) dc.getLocalAddress();
// state
assertTrue(s.isBound());
assertFalse(s.isConnected());
assertFalse(s.isClosed());
// local address
assertEquals(s.getLocalAddress(), localAddress.getAddress());
assertTrue(s.getLocalPort() == localAddress.getPort());
assertEquals(s.getLocalSocketAddress(), localAddress);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
assertTrue((s.getRemoteSocketAddress() == null));
} finally {
dc.close();
}
// state
assertTrue(s.isBound());
assertFalse(s.isConnected());
assertTrue(s.isClosed());
// local address
assertTrue(s.getLocalAddress() == null);
assertTrue(s.getLocalPort() == -1);
assertTrue(s.getLocalSocketAddress() == null);
// remote address
assertTrue(s.getInetAddress() == null);
assertTrue(s.getPort() == -1);
assertTrue((s.getRemoteSocketAddress() == null));
}
}

View file

@ -22,7 +22,7 @@
*/ */
/* @test /* @test
* @bug 7184932 * @bug 7184932 8232673
* @summary Test asynchronous close and interrupt of timed socket adapter methods * @summary Test asynchronous close and interrupt of timed socket adapter methods
* @key randomness intermittent * @key randomness intermittent
*/ */
@ -78,8 +78,10 @@ public class AdaptorCloseAndInterrupt {
try (DatagramChannel peer = DatagramChannel.open()) { try (DatagramChannel peer = DatagramChannel.open()) {
peer.socket().bind(null); peer.socket().bind(null);
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(); new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(0);
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(); new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(30_000);
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(0);
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(30_000);
} }
new AdaptorCloseAndInterrupt().ssAcceptAsyncClose(); new AdaptorCloseAndInterrupt().ssAcceptAsyncClose();
@ -138,11 +140,10 @@ public class AdaptorCloseAndInterrupt {
} }
} }
void dcReceiveAsyncClose() throws IOException { void dcReceiveAsyncClose(int timeout) throws IOException {
DatagramChannel dc = DatagramChannel.open(); DatagramChannel dc = DatagramChannel.open();
dc.connect(new InetSocketAddress( dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
InetAddress.getLoopbackAddress(), port)); dc.socket().setSoTimeout(timeout);
dc.socket().setSoTimeout(30*1000);
doAsyncClose(dc); doAsyncClose(dc);
@ -150,24 +151,23 @@ public class AdaptorCloseAndInterrupt {
dc.socket().receive(new DatagramPacket(new byte[100], 100)); dc.socket().receive(new DatagramPacket(new byte[100], 100));
System.err.format("close() was invoked: %s%n", isClosed.get()); System.err.format("close() was invoked: %s%n", isClosed.get());
throw new RuntimeException("receive should not have completed"); throw new RuntimeException("receive should not have completed");
} catch (ClosedChannelException expected) {} } catch (SocketException expected) { }
if (!dc.socket().isClosed()) if (!dc.socket().isClosed())
throw new RuntimeException("socket is not closed"); throw new RuntimeException("socket is not closed");
} }
void dcReceiveAsyncInterrupt() throws IOException { void dcReceiveAsyncInterrupt(int timeout) throws IOException {
DatagramChannel dc = DatagramChannel.open(); DatagramChannel dc = DatagramChannel.open();
dc.connect(new InetSocketAddress( dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
InetAddress.getLoopbackAddress(), port)); dc.socket().setSoTimeout(timeout);
dc.socket().setSoTimeout(30*1000);
doAsyncInterrupt(); doAsyncInterrupt();
try { try {
dc.socket().receive(new DatagramPacket(new byte[100], 100)); dc.socket().receive(new DatagramPacket(new byte[100], 100));
throw new RuntimeException("receive should not have completed"); throw new RuntimeException("receive should not have completed");
} catch (ClosedByInterruptException expected) { } catch (SocketException expected) {
System.out.format("interrupt() was invoked: %s%n", System.out.format("interrupt() was invoked: %s%n",
isInterrupted.get()); isInterrupted.get());
System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n", System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n",