8241305: Add protocol specific factory creation methods to SocketChannel and ServerSocketChannel

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Michael McMahon 2020-05-17 21:15:33 +01:00
parent 42bad03de8
commit 0f7aeed416
12 changed files with 1044 additions and 33 deletions

View file

@ -33,6 +33,7 @@ import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import static java.util.Objects.requireNonNull;
/**
* A selectable channel for datagram-oriented sockets.
@ -184,7 +185,7 @@ public abstract class DatagramChannel
* @since 1.7
*/
public static DatagramChannel open(ProtocolFamily family) throws IOException {
return SelectorProvider.provider().openDatagramChannel(family);
return SelectorProvider.provider().openDatagramChannel(requireNonNull(family));
}
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2020, 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
@ -26,11 +26,13 @@
package java.nio.channels;
import java.io.IOException;
import java.net.ProtocolFamily;
import java.net.ServerSocket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import static java.util.Objects.requireNonNull;
/**
* A selectable channel for stream-oriented listening sockets.
@ -113,6 +115,34 @@ public abstract class ServerSocketChannel
return SelectorProvider.provider().openServerSocketChannel();
}
/**
* Opens a server-socket channel.The {@code family} parameter specifies the
* {@link ProtocolFamily protocol family} of the channel's socket.
*
* <p> The new channel is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openServerSocketChannel(ProtocolFamily)
* openServerSocketChannel(ProtocolFamily)} method of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object. </p>
*
* @param family
* The protocol family
*
* @return A new socket channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported. For example,
* suppose the parameter is specified as {@link
* java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6}
* but IPv6 is not enabled on the platform.
* @throws IOException
* If an I/O error occurs
*
* @since 15
*/
public static ServerSocketChannel open(ProtocolFamily family) throws IOException {
return SelectorProvider.provider().openServerSocketChannel(requireNonNull(family));
}
/**
* Returns an operation set identifying this channel's supported
* operations.

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2020, 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
@ -26,12 +26,14 @@
package java.nio.channels;
import java.io.IOException;
import java.net.ProtocolFamily;
import java.net.Socket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import static java.util.Objects.requireNonNull;
/**
* A selectable channel for stream-oriented connecting sockets.
@ -150,6 +152,34 @@ public abstract class SocketChannel
return SelectorProvider.provider().openSocketChannel();
}
/**
* Opens a socket channel. The {@code family} parameter specifies the
* {@link ProtocolFamily protocol family} of the channel's socket.
*
* <p> The new channel is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openSocketChannel(ProtocolFamily)
* openSocketChannel(ProtocolFamily)} method of the system-wide default.
* {@link java.nio.channels.spi.SelectorProvider} object.</p>
*
* @param family
* The protocol family
*
* @return A new socket channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported. For example,
* suppose the parameter is specified as {@link
* java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6}
* but IPv6 is not enabled on the platform.
* @throws IOException
* If an I/O error occurs
*
* @since 15
*/
public static SocketChannel open(ProtocolFamily family) throws IOException {
return SelectorProvider.provider().openSocketChannel(requireNonNull(family));
}
/**
* Opens a socket channel and connects it to a remote address.
*

View file

@ -36,6 +36,7 @@ import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
@ -315,4 +316,51 @@ public abstract class SelectorProvider {
return null;
}
/**
* Opens a socket channel.
*
* @implSpec The default implementation of this method first checks that
* the given protocol {@code family} is not {@code null},
* then throws {@link UnsupportedOperationException}.
*
* @param family
* The protocol family
*
* @return The new channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported
* @throws IOException
* If an I/O error occurs
*
* @since 15
*/
public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
throw new UnsupportedOperationException("Protocol family not supported");
}
/**
* Opens a server-socket channel.
*
* @implSpec The default implementation of this method first checks that
* the given protocol {@code family} is not {@code null},
* then throws {@link UnsupportedOperationException}.
*
* @param family
* The protocol family
*
* @return The new channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported
* @throws IOException
* If an I/O error occurs
*
* @since 15
*/
public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
throw new UnsupportedOperationException("Protocol family not supported");
}
}

View file

@ -249,6 +249,57 @@ public class Net {
port);
}
private static final InetAddress anyLocalInet4Address;
private static final InetAddress anyLocalInet6Address;
private static final InetAddress inet4LoopbackAddress;
private static final InetAddress inet6LoopbackAddress;
static {
try {
anyLocalInet4Address = inet4FromInt(0);
assert anyLocalInet4Address instanceof Inet4Address
&& anyLocalInet4Address.isAnyLocalAddress();
anyLocalInet6Address = InetAddress.getByAddress(new byte[16]);
assert anyLocalInet6Address instanceof Inet6Address
&& anyLocalInet6Address.isAnyLocalAddress();
inet4LoopbackAddress = inet4FromInt(0x7f000001);
assert inet4LoopbackAddress instanceof Inet4Address
&& inet4LoopbackAddress.isLoopbackAddress();
byte[] bytes = new byte[16];
bytes[15] = 0x01;
inet6LoopbackAddress = InetAddress.getByAddress(bytes);
assert inet6LoopbackAddress instanceof Inet6Address
&& inet6LoopbackAddress.isLoopbackAddress();
} catch (Exception e) {
throw new InternalError(e);
}
}
static InetAddress inet4LoopbackAddress() {
return inet4LoopbackAddress;
}
static InetAddress inet6LoopbackAddress() {
return inet6LoopbackAddress;
}
/**
* Returns the wildcard address that corresponds to the given protocol family.
*
* @see InetAddress#isAnyLocalAddress()
*/
static InetAddress anyLocalAddress(ProtocolFamily family) {
if (family == StandardProtocolFamily.INET) {
return anyLocalInet4Address;
} else if (family == StandardProtocolFamily.INET6) {
return anyLocalInet6Address;
} else {
throw new IllegalArgumentException();
}
}
/**
* Returns any IPv4 address of the given network interface, or
* null if the interface does not have any IPv4 addresses.
@ -467,7 +518,13 @@ public class Net {
}
static FileDescriptor serverSocket(boolean stream) {
return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback));
return serverSocket(UNSPEC, stream);
}
static FileDescriptor serverSocket(ProtocolFamily family, boolean stream) {
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return IOUtil.newFD(socket0(preferIPv6, stream, true, fastLoopback));
}
// Due to oddities SO_REUSEADDR on windows reuse is ignored

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2020, 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
@ -72,4 +72,14 @@ public abstract class SelectorProviderImpl
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
@Override
public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
return new SocketChannelImpl(this, family);
}
@Override
public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) {
return new ServerSocketChannelImpl(this, family);
}
}

View file

@ -28,10 +28,12 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.net.SocketTimeoutException;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AsynchronousCloseException;
@ -62,6 +64,9 @@ class ServerSocketChannelImpl
// Used to make native close and configure calls
private static final NativeDispatcher nd = new SocketDispatcher();
// The protocol family of the socket
private final ProtocolFamily family;
// Our file descriptor
private final FileDescriptor fd;
private final int fdVal;
@ -95,10 +100,26 @@ class ServerSocketChannelImpl
// -- End of fields protected by stateLock
ServerSocketChannelImpl(SelectorProvider sp) {
this(sp, Net.isIPv6Available()
? StandardProtocolFamily.INET6
: StandardProtocolFamily.INET);
}
ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family) {
super(sp);
this.fd = Net.serverSocket(true);
Objects.requireNonNull(family, "'family' is null");
if ((family != StandardProtocolFamily.INET) &&
(family != StandardProtocolFamily.INET6)) {
throw new UnsupportedOperationException("Protocol family not supported");
}
if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) {
throw new UnsupportedOperationException("IPv6 not available");
}
this.family = family;
this.fd = Net.serverSocket(family, true);
this.fdVal = IOUtil.fdVal(fd);
}
@ -106,8 +127,13 @@ class ServerSocketChannelImpl
throws IOException
{
super(sp);
this.family = Net.isIPv6Available()
? StandardProtocolFamily.INET6
: StandardProtocolFamily.INET;
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
if (bound) {
synchronized (stateLock) {
localAddress = Net.localAddress(fd);
@ -210,14 +236,17 @@ class ServerSocketChannelImpl
ensureOpen();
if (localAddress != null)
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null)
? new InetSocketAddress(0)
: Net.checkAddress(local);
InetSocketAddress isa;
if (local == null) {
isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
} else {
isa = Net.checkAddress(local, family);
}
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.bind(family, fd, isa.getAddress(), isa.getPort());
Net.listen(fd, backlog < 1 ? 50 : backlog);
localAddress = Net.localAddress(fd);
}
@ -358,7 +387,7 @@ class ServerSocketChannelImpl
if (sm != null) {
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
}
return new SocketChannelImpl(provider(), newfd, isa);
return new SocketChannelImpl(provider(), family, newfd, isa);
} catch (Exception e) {
nd.close(newfd);
throw e;

View file

@ -28,6 +28,7 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.Socket;
@ -71,6 +72,9 @@ class SocketChannelImpl
// Used to make native read and write calls
private static final NativeDispatcher nd = new SocketDispatcher();
// The protocol family of the socket
private final ProtocolFamily family;
// Our file descriptor object
private final FileDescriptor fd;
private final int fdVal;
@ -118,12 +122,26 @@ class SocketChannelImpl
// -- End of fields protected by stateLock
// Constructor for normal connecting sockets
//
SocketChannelImpl(SelectorProvider sp) throws IOException {
this(sp, Net.isIPv6Available()
? StandardProtocolFamily.INET6
: StandardProtocolFamily.INET);
}
SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException {
super(sp);
this.fd = Net.socket(true);
Objects.requireNonNull(family, "'family' is null");
if ((family != StandardProtocolFamily.INET) &&
(family != StandardProtocolFamily.INET6)) {
throw new UnsupportedOperationException("Protocol family not supported");
}
if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) {
throw new UnsupportedOperationException("IPv6 not available");
}
this.family = family;
this.fd = Net.socket(family, true);
this.fdVal = IOUtil.fdVal(fd);
}
@ -131,8 +149,12 @@ class SocketChannelImpl
throws IOException
{
super(sp);
this.family = Net.isIPv6Available()
? StandardProtocolFamily.INET6
: StandardProtocolFamily.INET;
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
if (bound) {
synchronized (stateLock) {
this.localAddress = Net.localAddress(fd);
@ -142,10 +164,14 @@ class SocketChannelImpl
// Constructor for sockets obtained from server sockets
//
SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa)
SocketChannelImpl(SelectorProvider sp,
ProtocolFamily family,
FileDescriptor fd,
InetSocketAddress isa)
throws IOException
{
super(sp);
this.family = family;
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
synchronized (stateLock) {
@ -225,8 +251,6 @@ class SocketChannelImpl
ensureOpen();
if (name == StandardSocketOptions.IP_TOS) {
ProtocolFamily family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
Net.setSocketOption(fd, family, name, value);
return this;
}
@ -260,10 +284,8 @@ class SocketChannelImpl
return (T)Boolean.valueOf(isReuseAddress);
}
// special handling for IP_TOS: always return 0 when IPv6
// special handling for IP_TOS
if (name == StandardSocketOptions.IP_TOS) {
ProtocolFamily family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
return (T) Net.getSocketOption(fd, family, name);
}
@ -632,14 +654,18 @@ class SocketChannelImpl
throw new ConnectionPendingException();
if (localAddress != null)
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null) ?
new InetSocketAddress(0) : Net.checkAddress(local);
InetSocketAddress isa;
if (local == null) {
isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
} else {
isa = Net.checkAddress(local, family);
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkListen(isa.getPort());
}
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.bind(family, fd, isa.getAddress(), isa.getPort());
localAddress = Net.localAddress(fd);
}
} finally {
@ -723,14 +749,21 @@ class SocketChannelImpl
/**
* Checks the remote address to which this channel is to be connected.
*/
private InetSocketAddress checkRemote(SocketAddress sa) throws IOException {
InetSocketAddress isa = Net.checkAddress(sa);
private InetSocketAddress checkRemote(SocketAddress sa) {
InetSocketAddress isa = Net.checkAddress(sa, family);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
}
if (isa.getAddress().isAnyLocalAddress()) {
return new InetSocketAddress(InetAddress.getLocalHost(), isa.getPort());
InetAddress address = isa.getAddress();
if (address.isAnyLocalAddress()) {
int port = isa.getPort();
if (address instanceof Inet4Address) {
return new InetSocketAddress(Net.inet4LoopbackAddress(), port);
} else {
assert family == StandardProtocolFamily.INET6;
return new InetSocketAddress(Net.inet6LoopbackAddress(), port);
}
} else {
return isa;
}
@ -748,7 +781,10 @@ class SocketChannelImpl
boolean connected = false;
try {
beginConnect(blocking, isa);
int n = Net.connect(fd, isa.getAddress(), isa.getPort());
int n = Net.connect(family,
fd,
isa.getAddress(),
isa.getPort());
if (n > 0) {
connected = true;
} else if (blocking) {