changeset 58680:7e9e2f10a050 datagramsocketimpl-branch

datagramsocketimpl-branch: initial code changes
author chegar
date Thu, 17 Oct 2019 21:15:33 +0100
parents 9c3209ff7550
children 2b1e684c3ce6
files src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java src/java.base/share/classes/java/net/DatagramSocket.java src/java.base/share/classes/java/net/DatagramSocketImpl.java src/java.base/share/classes/sun/net/PlatformDatagramSocketImpl.java src/java.base/share/classes/sun/nio/ch/NioDatagramSocketImpl.java src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java src/java.base/unix/native/libnio/ch/NioDatagramSocketImpl.c test/jdk/java/net/DatagramSocket/B6411513.java
diffstat 9 files changed, 1234 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Thu Oct 17 21:15:33 2019 +0100
@@ -30,7 +30,7 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
-
+import sun.net.PlatformDatagramSocketImpl;
 import sun.net.ResourceManager;
 import sun.net.ext.ExtendedSocketOptions;
 import sun.net.util.IPAddressUtil;
@@ -46,13 +46,13 @@
  * @author Pavani Diwanji
  */
 
-abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
+abstract class AbstractPlainDatagramSocketImpl extends PlatformDatagramSocketImpl
 {
     /* timeout value for receive() */
-    int timeout = 0;
-    boolean connected = false;
-    private int trafficClass = 0;
-    protected InetAddress connectedAddress = null;
+    int timeout;
+    boolean connected;
+    private int trafficClass;
+    protected InetAddress connectedAddress;
     private int connectedPort = -1;
 
     private static final String os =
@@ -84,6 +84,12 @@
         return isReusePortAvailable;
     }
 
+    private final boolean isMulticast;
+
+    AbstractPlainDatagramSocketImpl(boolean isMulticast) {
+        this.isMulticast = isMulticast;
+    }
+
     /**
      * Creates a datagram socket
      */
@@ -430,7 +436,7 @@
 
     @Override
     protected Set<SocketOption<?>> supportedOptions() {
-        if (getDatagramSocket() instanceof MulticastSocket)
+        if (isMulticast)
             return multicastSocketOptions;
         else
             return datagramSocketOptions;
@@ -523,7 +529,8 @@
     protected abstract void connect0(InetAddress address, int port) throws SocketException;
     protected abstract void disconnect0(int family);
 
-    protected boolean nativeConnectDisabled() {
+    @Override
+    public boolean nativeConnectDisabled() {
         return connectDisabled;
     }
 
--- a/src/java.base/share/classes/java/net/DatagramSocket.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java	Thu Oct 17 21:15:33 2019 +0100
@@ -25,6 +25,8 @@
 
 package java.net;
 
+import sun.net.PlatformDatagramSocketImpl;
+
 import java.io.IOException;
 import java.nio.channels.DatagramChannel;
 import java.security.AccessController;
@@ -147,8 +149,8 @@
           bind(new InetSocketAddress(0));
 
         // old impls do not support connect/disconnect
-        if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
-             ((AbstractPlainDatagramSocketImpl)impl).nativeConnectDisabled())) {
+        if (oldImpl || (impl instanceof PlatformDatagramSocketImpl &&
+             ((PlatformDatagramSocketImpl)impl).nativeConnectDisabled())) {
             connectState = ST_CONNECTED_NO_IMPL;
         } else {
             try {
@@ -338,7 +340,6 @@
         }
         // creates a udp socket
         impl.create();
-        impl.setDatagramSocket(this);
         created = true;
     }
 
--- a/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Thu Oct 17 21:15:33 2019 +0100
@@ -48,20 +48,6 @@
      */
     protected FileDescriptor fd;
 
-    /**
-     * The DatagramSocket or MulticastSocket
-     * that owns this impl
-     */
-    DatagramSocket socket;
-
-    void setDatagramSocket(DatagramSocket socket) {
-        this.socket = socket;
-    }
-
-    DatagramSocket getDatagramSocket() {
-        return socket;
-    }
-
     int dataAvailable() {
         // default impl returns zero, which disables the calling
         // functionality
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/net/PlatformDatagramSocketImpl.java	Thu Oct 17 21:15:33 2019 +0100
@@ -0,0 +1,32 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.net;
+
+import java.net.DatagramSocketImpl;
+
+public abstract class  PlatformDatagramSocketImpl extends DatagramSocketImpl {
+    public abstract boolean nativeConnectDisabled();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/NioDatagramSocketImpl.java	Thu Oct 17 21:15:33 2019 +0100
@@ -0,0 +1,905 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ProtocolFamily;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketOption;
+import java.net.SocketOptions;
+import java.net.SocketTimeoutException;
+import java.net.StandardProtocolFamily;
+import java.net.StandardSocketOptions;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+import jdk.internal.ref.CleanerFactory;
+import sun.net.PlatformDatagramSocketImpl;
+import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
+import sun.net.util.IPAddressUtil;
+import sun.security.action.GetPropertyAction;
+import static java.net.StandardProtocolFamily.INET6;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+/**
+ * A DatagramSocketImpl based on low-level NIO primitives.
+ */
+public class NioDatagramSocketImpl extends PlatformDatagramSocketImpl {
+
+    private static final NativeDispatcher nd = new SocketDispatcher();
+
+    private static final int MAX_PACKET_LEN = 65536;
+
+    private static final ProtocolFamily family = family();
+
+    // Lock held by current reading or connecting thread
+    private final ReentrantLock readLock = new ReentrantLock();
+
+    // Lock held by current writing or connecting thread
+    private final ReentrantLock writeLock = new ReentrantLock();
+
+    // The stateLock for read/changing state
+    private final Object stateLock = new Object();
+    private static final int ST_NEW = 0;
+    private static final int ST_UNCONNECTED = 1;
+    private static final int ST_CONNECTING = 2;
+    private static final int ST_CONNECTED = 3;
+    private static final int ST_CLOSING = 4;
+    private static final int ST_CLOSED = 5;
+    private volatile int state;  // need stateLock to change
+
+    // set by create, protected by stateLock
+    private FileDescriptorCloser closer;
+
+    // set to true when the socket is in non-blocking mode
+    private volatile boolean nonBlocking;
+
+    // used by connect/read/write/accept, protected by stateLock
+    private long readerThread;
+    private long writerThread;
+
+    // Binding and remote address (when connected)
+    private InetSocketAddress remoteAddress;
+
+    // receive timeout in millis
+    private volatile int timeout;
+
+    /**  Returns true if the socket is open. */
+    private boolean isOpen() {
+        return state < ST_CLOSING;
+    }
+
+    /** Throws SocketException if the socket is not open. */
+    private void ensureOpen() throws SocketException {
+        int state = this.state;
+        if (state == ST_NEW)
+            throw new SocketException("Socket not created");
+        if (state >= ST_CLOSING)
+            throw new SocketException("Socket closed");
+    }
+
+    /**
+     * Returns the socket protocol family.
+     */
+    private static ProtocolFamily family() {
+        if (Net.isIPv6Available()) {
+            return StandardProtocolFamily.INET6;
+        } else {
+            return StandardProtocolFamily.INET;
+        }
+    }
+
+    @Override
+    protected void create() throws SocketException {
+        synchronized (stateLock) {
+            if (state != ST_NEW)
+                throw new SocketException("Already created");
+            ResourceManager.beforeUdpCreate();
+            FileDescriptor fd;
+            try {
+                fd = Net.socket(false);
+            } catch (IOException ioe) {
+                ResourceManager.afterUdpClose();
+                SocketException se = new SocketException(ioe.getMessage());
+                se.initCause(ioe);
+                throw se;
+            }
+            this.fd = fd;
+            this.closer = FileDescriptorCloser.create(this);
+            this.state = ST_UNCONNECTED;
+        }
+    }
+
+    @Override
+    protected void bind(int port, InetAddress addr) throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            if (localPort != 0)
+                throw new SocketException("Already bound");
+            try {
+                Net.bind(fd, addr, port);
+                localPort = Net.localAddress(fd).getPort();
+            } catch (SocketException e) {
+                throw e;
+            } catch (IOException ioe) {
+                SocketException se = new SocketException(ioe.getMessage());
+                se.initCause(ioe);
+                throw se;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
+        throw (E) e;
+    }
+
+    @Override
+    protected int peek(InetAddress i) {
+        DatagramPacket packet = new DatagramPacket(new byte[1], 0, 1);
+        try {
+            receive(packet, true);
+            return packet.getPort();
+        } catch (IOException e) {
+            sneakyThrow(e);
+            throw new InternalError("should not reach here");
+        }
+    }
+
+    @Override
+    protected int peekData(DatagramPacket packet) {
+        try {
+            receive(packet, true);
+            return packet.getPort();
+        } catch (IOException e) {
+            sneakyThrow(e);
+            throw new InternalError("should not reach here");
+        }
+    }
+
+    /**
+     * Disables the current thread for scheduling purposes until the socket is
+     * ready for I/O, or is asynchronously closed, for up to the specified
+     * waiting time.
+     * @throws IOException if an I/O error occurs
+     */
+    private void park(int event, long nanos) throws IOException {
+        long millis;
+        if (nanos == 0) {
+            millis = -1;
+        } else {
+            millis = NANOSECONDS.toMillis(nanos);
+        }
+        Net.poll(fd, event, millis);
+    }
+
+    /**
+     * Disables the current thread for scheduling purposes until the socket is
+     * ready for I/O or is asynchronously closed.
+     * @throws IOException if an I/O error occurs
+     */
+    private void park(int event) throws IOException {
+        park(event, 0);
+    }
+
+    /**
+     * Marks the beginning of a write operation that might block.
+     * @throws SocketException if the socket is closed or not connected
+     */
+    private InetSocketAddress beginWrite() throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            writerThread = NativeThread.current();
+            return remoteAddress;
+        }
+    }
+    /**
+     * Marks the end of a write operation that may have blocked.
+     */
+    private void endWrite(boolean completed) throws SocketException {
+        synchronized (stateLock) {
+            writerThread = 0;
+            int state = this.state;
+            if (state == ST_CLOSING)
+                tryFinishClose();
+            if (!completed && state >= ST_CLOSING)
+                throw new SocketException("Socket closed");
+        }
+    }
+
+    /**
+     * Attempts to send bytes from the given byte array, to the given (optional)
+     * address.
+     */
+    private int trySend(byte[] b, int off, int len, InetAddress address, int port)
+        throws IOException
+    {
+        ByteBuffer src = Util.getTemporaryDirectBuffer(len);
+        assert src.position() == 0 : "Expected source position of 0, in " + src;
+        assert src.remaining() == len : "Expected remaining " + len  + ", in " + src;
+        try {
+            src.put(b, off, len);
+            return send0(true, fd, ((DirectBuffer)src).address(), len, address, port);
+        } finally {
+            Util.offerFirstTemporaryDirectBuffer(src);
+        }
+    }
+
+    @Override
+    protected void send(DatagramPacket p) throws IOException {
+        Objects.requireNonNull(p);
+        InetSocketAddress target = Net.checkAddress(p.getSocketAddress());
+        byte[] b = p.getData();
+        int off = p.getOffset();
+        int len = p.getLength();
+        if (len > MAX_PACKET_LEN)
+            len = MAX_PACKET_LEN;
+
+        writeLock.lock();
+        try {
+            int n = 0;
+            InetAddress targetAddress = null;
+            int targetPort = 0;
+            try {
+                SocketAddress remote = beginWrite();
+                if (remote != null) {
+                    // connected
+                    if (!target.equals(remote)) {
+                        String msg = "Connected address and packet address differ";
+                        throw new IllegalArgumentException(msg);
+                    }
+                } else {
+                    // not connected
+                    if (target.getAddress().isLinkLocalAddress())
+                        target = IPAddressUtil.toScopedAddress(target);
+                    targetAddress = target.getAddress();
+                    targetPort = target.getPort();
+                }
+                n = trySend(b, off, len, targetAddress, targetPort);
+                while (IOStatus.okayToRetry(n) && isOpen()) {
+                    park(Net.POLLOUT);
+                    n = trySend(b, off, len, targetAddress, targetPort);
+                }
+            } finally {
+                endWrite(n > 0);
+                assert n >= 0;
+            }
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    /**
+     * Configures the socket to blocking mode. This method is a no-op if the
+     * socket is already in blocking mode.
+     * @throws IOException if closed or there is an I/O error changing the mode
+     */
+    private void configureBlocking() throws IOException {
+        assert readLock.isHeldByCurrentThread();
+        if (nonBlocking) {
+            synchronized (stateLock) {
+                ensureOpen();
+                IOUtil.configureBlocking(fd, true);
+                nonBlocking = false;
+            }
+        }
+    }
+
+    /**
+     * Configures the socket to non-blocking mode. This method is a no-op if the
+     * socket is already in non-blocking mode.
+     * @throws IOException if closed or there is an I/O error changing the mode
+     */
+    private void configureNonBlocking() throws IOException {
+        assert readLock.isHeldByCurrentThread();
+        if (!nonBlocking) {
+            synchronized (stateLock) {
+                ensureOpen();
+                IOUtil.configureBlocking(fd, false);
+                nonBlocking = true;
+            }
+        }
+    }
+
+    private InetSocketAddress beginRead() throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            readerThread = NativeThread.current();
+            return remoteAddress;
+        }
+    }
+
+    private void endRead(boolean completed) throws SocketException {
+        synchronized (stateLock) {
+            readerThread = 0;
+            int state = this.state;
+            if (state == ST_CLOSING)
+                tryFinishClose();
+            if (!completed && state >= ST_CLOSING)
+                throw new SocketException("Socket closed");
+        }
+    }
+
+    private InetSocketAddress sender = new InetSocketAddress(0);  // Set by receive0
+
+    /**
+     * Attempts to read bytes from the socket into the given byte array.
+     */
+    private int tryReceive(byte[] b, int off, int len, boolean isPeek)
+        throws IOException
+    {
+        ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
+        assert dst.position() == 0;
+        assert dst.remaining() >= len;
+        try {
+            int n = receive0(fd, ((DirectBuffer)dst).address(), len, isPeek,
+                             sender.getAddress(), sender.getPort());
+            assert n <= len : "received:" + n + ", expected len:" + len;
+            if (n > 0) {
+                dst.get(b, off, n);
+            }
+            return n;
+        } finally {
+            Util.offerFirstTemporaryDirectBuffer(dst);
+        }
+    }
+
+    /**
+     * Reads bytes from the socket into the given byte array with a timeout.
+     * @throws SocketTimeoutException if the read timeout elapses
+     */
+    private int timedReceive(byte[] b, int off, int len, long nanos, boolean isPeek)
+        throws IOException
+    {
+        long startNanos = System.nanoTime();
+        int n = tryReceive(b, off, len, isPeek);
+        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 = tryReceive(b, off, len, isPeek);
+        }
+        return n;
+    }
+
+    @Override
+    protected void receive(DatagramPacket p) throws IOException {
+        receive(p, false);
+    }
+
+    private void receive(DatagramPacket p, boolean isPeek) throws IOException {
+        Objects.requireNonNull(p);
+        byte[] b = p.getData();
+        int off = p.getOffset();
+        int len = b.length - off;
+        assert len >= 0;
+        if (len > MAX_PACKET_LEN)
+            len = MAX_PACKET_LEN;
+
+        readLock.lock();
+        try {
+            int n = 0;
+            try {
+                SocketAddress remote = beginRead();
+                boolean connected = (remote != null);
+                int timeout = this.timeout;
+                if (timeout > 0) {
+                    // receive with timeout
+                    configureNonBlocking();
+                    long nanos = MILLISECONDS.toNanos(timeout);
+                    n = timedReceive(b, off, len, nanos, isPeek);
+                } else {
+                    // receive, no timeout
+                    n = tryReceive(b, off, len, isPeek);
+                    while (IOStatus.okayToRetry(n) && isOpen()) {
+                        park(Net.POLLIN);
+                        n = tryReceive(b, off, len, isPeek);
+                    }
+                }
+                assert n > 0;
+                assert sender != null;
+                if (p.getAddress() == null || !p.getAddress().equals(sender.getAddress()))
+                    p.setAddress(sender.getAddress());
+                if (p.getPort() != sender.getPort())
+                    p.setPort(sender.getPort());
+                p.setLength(n);
+            } catch (IOException e) {
+                // #### reset packet offset and length! ??
+                throw e;
+            } finally {
+                endRead(n > 0);
+                assert IOStatus.check(n);
+            }
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws SocketException {
+        readLock.lock();
+        try {
+            writeLock.lock();
+            try {
+                synchronized (stateLock) {
+                    ensureOpen();
+                    if (state == ST_CONNECTED) {
+                        // #### already connected? throw? or connect to new remote
+                    }
+
+                    int n = Net.connect(family, fd, address, port);
+                    if (n <= 0)
+                        throw new InternalError("should not reach here");
+
+                    remoteAddress = new InetSocketAddress(address, port);
+                    state = ST_CONNECTED;
+
+                    // refresh local address
+                    localPort = Net.localAddress(fd).getPort();
+
+                    // flush any packets already received.
+                    try {
+                        byte[] ba = new byte[1];
+                        configureNonBlocking();
+                        while (tryReceive(ba, 0, 1, false) > 0) { }
+                    } finally {
+                        configureBlocking();
+                    }
+                }
+            } catch (SocketException e) {
+                throw e;
+            } catch (IOException e) {
+                throw new SocketException(e.getMessage());
+            } finally {
+                writeLock.unlock();
+            }
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    protected void disconnect() {
+        readLock.lock();
+        try {
+            writeLock.lock();
+            try {
+                synchronized (stateLock) {
+                    if (!isOpen() || (state != ST_CONNECTED))
+                        return;
+
+                    try {
+                        disconnect0(fd, family == INET6);
+
+                        // no longer connected
+                        remoteAddress = null;
+                        state = ST_UNCONNECTED;
+
+                        // check whether rebind is needed
+                        InetSocketAddress isa = Net.localAddress(fd);
+                        if (isa.getPort() == 0) {
+                            // On Linux, if bound to ephemeral port,
+                            // disconnect does not preserve that port.
+                            // In this case, try to rebind to the previous port.
+                            int port = localPort;
+                            Net.bind(family, fd, isa.getAddress(), port);
+                            isa = Net.localAddress(fd); // refresh address
+                            assert isa.getPort() == port;
+                        }
+
+                        // refresh local port
+                        localPort = isa.getPort();
+                    } catch (IOException e) {
+                        sneakyThrow(e);
+                        throw new InternalError("should not reach here");
+                    }
+                }
+            } finally {
+                writeLock.unlock();
+            }
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Closes the socket if there are no I/O operations in progress.
+     */
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if (readerThread == 0 && writerThread == 0) {
+            try {
+                closer.run();
+            } catch (UncheckedIOException ioe) {
+                throw ioe.getCause();
+            } finally {
+                state = ST_CLOSED;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes tryClose to attempt to close the socket.
+     *
+     * This method is used for deferred closing by I/O operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
+
+    /**
+     * Closes the socket. If there are I/O operations in progress then the
+     * socket is pre-closed and the threads are signalled. The socket will be
+     * closed when the last I/O operation aborts.
+     */
+    @Override
+    protected void close() {
+        synchronized (stateLock) {
+            int state = this.state;
+            if (state >= ST_CLOSING)
+                return;
+            if (state == ST_NEW) {
+                this.state = ST_CLOSED;
+                return;
+            }
+            this.state = ST_CLOSING;
+
+            // Attempt to close the socket. If there are I/O operations in
+            // progress then the socket is pre-closed and the thread(s)
+            // signalled. The last thread will close the file descriptor.
+            try {
+                if (!tryClose()) {
+                    nd.preClose(fd);
+                    long reader = readerThread;
+                    if (reader != 0)
+                        NativeThread.signal(reader);
+                    long writer = writerThread;
+                    if (writer != 0)
+                        NativeThread.signal(writer);
+                }
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);  // Ugh!
+            }
+        }
+    }
+
+    private static final Set<SocketOption<?>> socketOptions = socketOptions();
+
+    private static Set<SocketOption<?>> socketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_SNDBUF);
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.IP_TOS);
+        if (Net.isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.datagramSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        return socketOptions;
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+        if (!supportedOptions().contains(opt))
+            throw new UnsupportedOperationException("'" + opt + "' not supported");
+        if (!opt.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
+        synchronized (stateLock) {
+            ensureOpen();
+            if (opt == StandardSocketOptions.IP_TOS) {
+                // maps to IP_TOS or IPV6_TCLASS
+                Net.setSocketOption(fd, family(), opt, value);
+            } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+                setOption(SocketOptions.SO_REUSEADDR, value);
+            } else if (opt == StandardSocketOptions.SO_REUSEPORT) {
+                setOption(SocketOptions.SO_REUSEPORT, value);
+            } else {
+                // option does not need special handling
+                Net.setSocketOption(fd, opt, value);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getOption(SocketOption<T> opt) throws IOException {
+        if (!supportedOptions().contains(opt))
+            throw new UnsupportedOperationException("'" + opt + "' not supported");
+        synchronized (stateLock) {
+            ensureOpen();
+            if (opt == StandardSocketOptions.IP_TOS) {
+                return (T) Net.getSocketOption(fd, family(), opt);
+            } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+                return (T) getOption(SocketOptions.SO_REUSEADDR);
+            } else if (opt == StandardSocketOptions.SO_REUSEPORT) {
+                return (T) getOption(SocketOptions.SO_REUSEPORT);
+            } else {
+                // option does not need special handling
+                return (T) Net.getSocketOption(fd, opt);
+            }
+        }
+    }
+
+    private static boolean booleanValue(Object value, String desc)
+        throws SocketException
+    {
+        if (!(value instanceof Boolean))
+            throw new SocketException("Bad value for " + desc);
+        return (boolean) value;
+    }
+
+    private static int intValue(Object value, String desc) throws SocketException {
+        if (!(value instanceof Integer))
+            throw new SocketException("Bad value for " + desc);
+        return (int) value;
+    }
+
+    @Override
+    public void setOption(int opt, Object value) throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            try {
+                switch (opt) {
+                case SO_TIMEOUT: {
+                    int i = intValue(value, "SO_TIMEOUT");
+                    if (i < 0)
+                        throw new IllegalArgumentException("timeout < 0");
+                    timeout = i;
+                    break;
+                }
+                case IP_TOS: {
+                    int i = intValue(value, "IP_TOS");
+                    Net.setSocketOption(fd, family, StandardSocketOptions.IP_TOS, i);
+                    break;
+                }
+                case SO_REUSEADDR: {
+                    boolean b = booleanValue(value, "SO_REUSEADDR");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEADDR, b);
+                    break;
+                }
+                case SO_BROADCAST: {
+                    boolean b = booleanValue(value, "SO_BROADCAST");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_BROADCAST, b);
+                    break;
+                }
+                case SO_BINDADDR: {
+                    throw new SocketException("Cannot re-bind Socket");
+                }
+                case SO_RCVBUF: {
+                    int i = intValue(value, "SO_RCVBUF");
+                    if (i <= 0)
+                        throw new SocketException("SO_RCVBUF <= 0");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_RCVBUF, i);
+                    break;
+                }
+                case SO_SNDBUF: {
+                    int i = intValue(value, "SO_SNDBUF");
+                    if (i <= 0)
+                        throw new SocketException("SO_SNDBUF <= 0");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_SNDBUF, i);
+                    break;
+                }
+                case SO_REUSEPORT: {
+                    if (!Net.isReusePortAvailable())
+                        throw new UnsupportedOperationException("SO_REUSEPORT not supported");
+                    boolean b = booleanValue(value, "SO_REUSEPORT");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEPORT, b);
+                    break;
+                }
+                default:
+                    throw new SocketException("unknown option: " + opt);
+                }
+            } catch (SocketException e) {
+                throw e;
+            } catch (IllegalArgumentException | IOException e) {
+                throw new SocketException(e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    public Object getOption(int opt) throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            try {
+                switch (opt) {
+                case SO_TIMEOUT:
+                    return timeout;
+                case IP_TOS:
+                    return Net.getSocketOption(fd, family(), StandardSocketOptions.IP_TOS);
+                case SO_BINDADDR:
+                    return Net.localAddress(fd).getAddress();
+                case SO_RCVBUF:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_RCVBUF);
+                case SO_SNDBUF:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_SNDBUF);
+                case SO_REUSEADDR:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEADDR);
+                case SO_BROADCAST:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_BROADCAST);
+                case SO_REUSEPORT:
+                    if (!Net.isReusePortAvailable())
+                        throw new UnsupportedOperationException("SO_REUSEPORT not supported");
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEPORT);
+                default:
+                    throw new SocketException("Unknown option " + opt);
+                }
+            } catch (SocketException e) {
+                throw e;
+            } catch (IllegalArgumentException | IOException e) {
+                throw new SocketException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * A task that closes a DatagramSocketImpl's file descriptor. The task is
+     * run when the NioDatagramSocketImpl is explicitly closed or when the
+     * NioDatagramSocketImpl becomes phantom reachable.
+     */
+    private static class FileDescriptorCloser implements Runnable {
+        private static final VarHandle CLOSED;
+        static {
+            try {
+                MethodHandles.Lookup l = MethodHandles.lookup();
+                CLOSED = l.findVarHandle(FileDescriptorCloser.class,
+                                         "closed",
+                                         boolean.class);
+            } catch (Exception e) {
+                throw new InternalError(e);
+            }
+        }
+        private volatile boolean closed;  // accessed through VarHandle
+        private final FileDescriptor fd;
+
+        FileDescriptorCloser(FileDescriptor fd) { this.fd = fd; }
+
+        static FileDescriptorCloser create(NioDatagramSocketImpl impl) {
+            assert Thread.holdsLock(impl.stateLock);
+            var closer = new FileDescriptorCloser(impl.fd);
+            CleanerFactory.cleaner().register(impl, closer);
+            return closer;
+        }
+
+        @Override
+        public void run() {
+            if (CLOSED.compareAndSet(this, false, true)) {
+                try {
+                    nd.close(fd);
+                } catch (IOException ioe) {
+                    throw new UncheckedIOException(ioe);
+                } finally {
+                    ResourceManager.afterUdpClose();
+                }
+            }
+        }
+    }
+
+    @Deprecated
+    @Override
+    protected void setTTL(byte ttl) {
+        throw new InternalError("should not reach here");
+    }
+
+    @Deprecated
+    @Override
+    protected byte getTTL() {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected void setTimeToLive(int ttl) {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected int getTimeToLive() {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected void join(InetAddress inetaddr) {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected void leave(InetAddress inetaddr) {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
+        throw new InternalError("should not reach here");
+    }
+
+    @Override
+    protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
+        throw new InternalError("should not reach here");
+    }
+
+    /** Set if the native connect() call is not to be used */
+    private static final boolean connectDisabled =
+            GetPropertyAction.privilegedGetProperty("os.name").contains("OS X");
+
+    @Override
+    public boolean nativeConnectDisabled() {
+        return connectDisabled;
+    }
+
+    // -- Native methods --
+
+    private static native void initIDs();
+
+    private native int receive0(FileDescriptor fd,
+                                long address,
+                                int len,
+                                boolean isPeek,
+                                InetAddress cachedSenderAddress,
+                                int cachedSenderPort)
+        throws IOException;
+
+    private static native int send0(boolean preferIPv6,
+                                    FileDescriptor fd,
+                                    long address,
+                                    int len,
+                                    InetAddress addr,
+                                    int port)
+        throws IOException;
+
+    private static native void disconnect0(FileDescriptor fd,
+                                           boolean isIPv6)
+        throws IOException;
+
+    static {
+        IOUtil.load();
+        initIDs();
+    }
+}
--- a/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java	Thu Oct 17 21:15:33 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -25,6 +25,10 @@
 
 package java.net;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.net.NetProperties;
+import sun.nio.ch.NioDatagramSocketImpl;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -52,13 +56,30 @@
         }
     }
 
+    private static final boolean USE_PLAINDATAGRAMSOCKETIMPL = usePlainDatagramSocketImpl();
+
+    private static boolean usePlainDatagramSocketImpl() {
+        PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
+        String s = AccessController.doPrivileged(pa);
+        return (s != null) && !s.equalsIgnoreCase("false");
+    }
+
+    /** Creates an instance of platform's DatagramSocketImpl */
+    static DatagramSocketImpl createPlatformSocketDatagramImpl(boolean isMulticast) {
+        if (USE_PLAINDATAGRAMSOCKETIMPL || isMulticast) {
+            return new PlainDatagramSocketImpl(isMulticast);
+        } else {
+            return new NioDatagramSocketImpl();
+        }
+    }
+
     /**
      * Creates a new <code>DatagramSocketImpl</code> instance.
      *
-     * @param   isMulticast     true if this impl if for a MutlicastSocket
+     * @param   isMulticast     true if this impl is for a MutlicastSocket
      * @return  a new instance of a <code>DatagramSocketImpl</code>.
      */
-    static DatagramSocketImpl createDatagramSocketImpl(boolean isMulticast /*unused on unix*/)
+    static DatagramSocketImpl createDatagramSocketImpl(boolean isMulticast)
         throws SocketException {
         if (prefixImplClass != null) {
             try {
@@ -69,7 +90,7 @@
                 throw new SocketException("can't instantiate DatagramSocketImpl");
             }
         } else {
-            return new java.net.PlainDatagramSocketImpl();
+            return createPlatformSocketDatagramImpl(isMulticast);
         }
     }
 }
--- a/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java	Thu Oct 17 21:15:33 2019 +0100
@@ -37,6 +37,10 @@
 
 class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
 {
+    PlainDatagramSocketImpl(boolean isMulticast) {
+        super(isMulticast);
+    }
+
     static {
         init();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/native/libnio/ch/NioDatagramSocketImpl.c	Thu Oct 17 21:15:33 2019 +0100
@@ -0,0 +1,212 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#if defined(__linux__) || defined(_ALLBSD_SOURCE)
+#include <netinet/in.h>
+#endif
+
+#include "net_util.h"
+#include "net_util_md.h"
+#include "nio.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_NioDatagramSocketImpl.h"
+
+static jfieldID dsi_senderID;   /* sender in sun.nio.ch.NioDatagramSocketImpl */
+static jclass isa_class;        /* java.net.InetSocketAddress */
+static jmethodID isa_ctorID;    /* InetSocketAddress(InetAddress, int) */
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_NioDatagramSocketImpl_initIDs(JNIEnv *env, jclass clazz)
+{
+    clazz = (*env)->FindClass(env, "java/net/InetSocketAddress");
+    CHECK_NULL(clazz);
+    isa_class = (*env)->NewGlobalRef(env, clazz);
+    if (isa_class == NULL) {
+        JNU_ThrowOutOfMemoryError(env, NULL);
+        return;
+    }
+    isa_ctorID = (*env)->GetMethodID(env, clazz, "<init>", "(Ljava/net/InetAddress;I)V");
+    CHECK_NULL(isa_ctorID);
+
+    clazz = (*env)->FindClass(env, "sun/nio/ch/NioDatagramSocketImpl");
+    CHECK_NULL(clazz);
+    dsi_senderID = (*env)->GetFieldID(env, clazz, "sender", "Ljava/net/InetSocketAddress;");
+    CHECK_NULL(dsi_senderID);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_NioDatagramSocketImpl_send0(JNIEnv *env,
+                                            jclass clazz,
+                                            jboolean preferIPv6,
+                                            jobject fdo,
+                                            jlong address,
+                                            jint len,
+                                            jobject destAddress,
+                                            jint destPort)
+{
+    jint fd = fdval(env, fdo);
+    void *buf = (void *)jlong_to_ptr(address);
+    SOCKETADDRESS sa;
+    struct sockaddr *saP = NULL;
+    int sa_len = 0;
+    jint n = 0;
+
+    if (destAddress != NULL) {  // not connected
+        if (NET_InetAddressToSockaddr(env, destAddress, destPort, &sa,
+                                      &sa_len, preferIPv6) != 0) {
+            return IOS_THROWN;
+        }
+        saP = &sa.sa;
+    }
+
+    n = sendto(fd, buf, len, 0, saP, sa_len);
+    if (n < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            return IOS_UNAVAILABLE;
+        }
+        if (errno == EINTR) {
+            return IOS_INTERRUPTED;
+        }
+        if (errno == ECONNREFUSED) {
+            JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
+            return IOS_THROWN;
+        }
+        return handleSocketError(env, errno);
+    }
+    return n;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_NioDatagramSocketImpl_receive0(JNIEnv *env,
+                                               jobject this,
+                                               jobject fdo,
+                                               jlong address,
+                                               jint len,
+                                               jboolean isPeek,
+                                               jobject cachedSenderAddress,
+                                               jint cachedSenderPort)
+{
+    jint fd = fdval(env, fdo);
+    void *buf = (void *)jlong_to_ptr(address);
+    SOCKETADDRESS sa;
+    socklen_t sa_len = sizeof(SOCKETADDRESS);
+    jboolean retry = JNI_FALSE;
+    int flags = 0;
+    jint n = 0;
+    jobject senderAddr;
+
+    if (isPeek == JNI_TRUE) {
+        flags = MSG_PEEK;
+    }
+
+    n = recvfrom(fd, buf, len, flags, &sa.sa, &sa_len);
+    if (n < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            return IOS_UNAVAILABLE;
+        } else if (errno == EINTR) {
+            return IOS_INTERRUPTED;
+        } else if (errno == ECONNREFUSED) {
+            JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
+                            "ICMP Port Unreachable");
+            return IOS_THROWN;
+        } else {
+            return handleSocketError(env, errno);
+        }
+    }
+
+    // If the cached address does not match, then create a new one.
+    if (cachedSenderAddress == NULL ||
+        !NET_SockaddrEqualsInetAddress(env, &sa, cachedSenderAddress) ||
+        cachedSenderPort != NET_GetPortFromSockaddr(&sa)) {
+
+        jobject isa = NULL;
+        int port = 0;
+        jobject ia = NET_SockaddrToInetAddress(env, &sa, &port);
+        if (ia != NULL) {
+            isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
+        }
+        CHECK_NULL_RETURN(isa, IOS_THROWN);
+
+        (*env)->SetObjectField(env, this, dsi_senderID, isa);
+    }
+
+    return n;
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_NioDatagramSocketImpl_disconnect0(JNIEnv *env,
+                                                 jobject clazz,
+                                                 jobject fdo,
+                                                 jboolean isIPv6)
+{
+    jint fd = fdval(env, fdo);
+    int rv;
+
+#if defined(__solaris__)
+    rv = connect(fd, 0, 0);
+#else
+    SOCKETADDRESS sa;
+    socklen_t len = isIPv6 ? sizeof(struct sockaddr_in6) :
+                             sizeof(struct sockaddr_in);
+
+    memset(&sa, 0, sizeof(sa));
+#if defined(_ALLBSD_SOURCE)
+    sa.sa.sa_family = isIPv6 ? AF_INET6 : AF_INET;
+#else
+    sa.sa.sa_family = AF_UNSPEC;
+#endif
+
+    rv = connect(fd, &sa.sa, len);
+
+#if defined(_ALLBSD_SOURCE)
+    if (rv < 0 && errno == EADDRNOTAVAIL)
+        rv = errno = 0;
+#elif defined(_AIX)
+    /* See W. Richard Stevens, "UNIX Network Programming, Volume 1", p. 254:
+     * 'Setting the address family to AF_UNSPEC might return EAFNOSUPPORT
+     * but that is acceptable.
+     */
+    if (rv < 0 && errno == EAFNOSUPPORT)
+        rv = errno = 0;
+#endif // defined(_ALLBSD_SOURCE) || defined(_AIX)
+
+#endif // defined(__solaris__)
+
+    if (rv < 0)
+        handleSocketError(env, errno);
+}
--- a/test/jdk/java/net/DatagramSocket/B6411513.java	Thu Oct 17 20:54:25 2019 +0100
+++ b/test/jdk/java/net/DatagramSocket/B6411513.java	Thu Oct 17 21:15:33 2019 +0100
@@ -28,7 +28,10 @@
  */
 
 import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
 import java.util.*;
+import static java.lang.System.out;
 
 public class B6411513 {
 
@@ -47,7 +50,7 @@
                     // out IPv6 address here. The test should be revisited
                     // later when aforementioned bug gets fixed.
                     if (addr instanceof Inet4Address) {
-                        System.out.printf("%s : %s\n", nic.getName(), addr);
+                        out.printf("%s : %s\n", nic.getName(), addr);
                         testConnectedUDP(addr);
                     }
                 }
@@ -65,20 +68,23 @@
         try {
             DatagramSocket s = new DatagramSocket(0, addr);
             DatagramSocket ss = new DatagramSocket(0, addr);
-            System.out.print("\tconnect...");
+            out.println("localaddress: " + s.getLocalSocketAddress());
+            out.println("\tconnect...");
             s.connect(ss.getLocalAddress(), ss.getLocalPort());
-            System.out.print("disconnect...");
+            out.println("localaddress: " + s.getLocalSocketAddress());
+            out.println("disconnect...");
             s.disconnect();
+            out.println("localaddress: " + s.getLocalSocketAddress());
 
             byte[] data = { 0, 1, 2 };
             DatagramPacket p = new DatagramPacket(data, data.length,
                     s.getLocalAddress(), s.getLocalPort());
             s.setSoTimeout( 10000 );
-            System.out.print("send...");
+            out.print("send...");
             s.send( p );
-            System.out.print("recv...");
+            out.print("recv...");
             s.receive( p );
-            System.out.println("OK");
+            out.println("OK");
 
             ss.close();
             s.close();
@@ -87,4 +93,29 @@
             throw e;
         }
     }
+
+    // Tests with DatagramChannel
+//    private static void testConnectedUDPNIO(InetAddress addr) throws Exception {
+//        DatagramChannel s = DatagramChannel.open();
+//        s.bind(new InetSocketAddress(addr, 0));
+//        DatagramChannel ss = DatagramChannel.open();
+//        ss.bind(new InetSocketAddress(addr, 0));
+//        out.println("localaddress: " + s.getLocalAddress());
+//        out.println("\tconnect...");
+//        s.connect(ss.getLocalAddress());
+//        out.println("localaddress: " + s.getLocalAddress());
+//        out.println("disconnect...");
+//        s.disconnect();
+//        out.println("localaddress: " + s.getLocalAddress());
+//
+//        byte[] data = {0, 1, 2};
+//        out.print("send...");
+//        s.send(ByteBuffer.wrap(data), s.getLocalAddress());
+//        out.print("recv...");
+//        s.receive(ByteBuffer.allocate(100));
+//        out.println("OK");
+//
+//        ss.close();
+//        s.close();
+//    }
 }