mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8253119: Remove the legacy PlainSocketImpl and PlainDatagramSocketImpl implementation
Reviewed-by: alanb, dfuchs, chegar
This commit is contained in:
parent
f485171ce8
commit
326b2e1344
78 changed files with 207 additions and 10924 deletions
|
@ -1,530 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 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 java.net;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.net.ResourceManager;
|
||||
import sun.net.ext.ExtendedSocketOptions;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* Abstract datagram and multicast socket implementation base class.
|
||||
* Note: This is not a public class, so that applets cannot call
|
||||
* into the implementation directly and hence cannot bypass the
|
||||
* security checks present in the DatagramSocket and MulticastSocket
|
||||
* classes.
|
||||
*
|
||||
* @author Pavani Diwanji
|
||||
*/
|
||||
|
||||
abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
||||
{
|
||||
/* timeout value for receive() */
|
||||
int timeout = 0;
|
||||
boolean connected = false;
|
||||
private int trafficClass = 0;
|
||||
protected InetAddress connectedAddress = null;
|
||||
private int connectedPort = -1;
|
||||
private final boolean isMulticast;
|
||||
|
||||
private static final String os =
|
||||
GetPropertyAction.privilegedGetProperty("os.name");
|
||||
|
||||
/**
|
||||
* flag set if the native connect() call not to be used
|
||||
*/
|
||||
private static final boolean connectDisabled = os.contains("OS X");
|
||||
|
||||
/**
|
||||
* Load net library into runtime.
|
||||
*/
|
||||
static {
|
||||
jdk.internal.loader.BootLoader.loadLibrary("net");
|
||||
}
|
||||
|
||||
private static volatile boolean checkedReusePort;
|
||||
private static volatile boolean isReusePortAvailable;
|
||||
|
||||
/**
|
||||
* Tells whether SO_REUSEPORT is supported.
|
||||
*/
|
||||
static boolean isReusePortAvailable() {
|
||||
if (!checkedReusePort) {
|
||||
isReusePortAvailable = isReusePortAvailable0();
|
||||
checkedReusePort = true;
|
||||
}
|
||||
return isReusePortAvailable;
|
||||
}
|
||||
|
||||
AbstractPlainDatagramSocketImpl(boolean isMulticast) {
|
||||
this.isMulticast = isMulticast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a datagram socket
|
||||
*/
|
||||
protected synchronized void create() throws SocketException {
|
||||
ResourceManager.beforeUdpCreate();
|
||||
fd = new FileDescriptor();
|
||||
try {
|
||||
datagramSocketCreate();
|
||||
SocketCleanable.register(fd, false);
|
||||
} catch (SocketException ioe) {
|
||||
ResourceManager.afterUdpClose();
|
||||
fd = null;
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a datagram socket to a local port.
|
||||
*/
|
||||
protected synchronized void bind(int lport, InetAddress laddr)
|
||||
throws SocketException {
|
||||
if (laddr.isLinkLocalAddress()) {
|
||||
laddr = IPAddressUtil.toScopedAddress(laddr);
|
||||
}
|
||||
bind0(lport, laddr);
|
||||
}
|
||||
|
||||
protected abstract void bind0(int lport, InetAddress laddr)
|
||||
throws SocketException;
|
||||
|
||||
/**
|
||||
* Sends a datagram packet. The packet contains the data and the
|
||||
* destination address to send the packet to.
|
||||
* @param p the packet to be sent.
|
||||
*/
|
||||
protected void send(DatagramPacket p) throws IOException {
|
||||
InetAddress orig = p.getAddress();
|
||||
if (orig.isLinkLocalAddress()) {
|
||||
InetAddress scoped = IPAddressUtil.toScopedAddress(orig);
|
||||
if (orig != scoped) {
|
||||
p = new DatagramPacket(p.getData(), p.getOffset(),
|
||||
p.getLength(), scoped, p.getPort());
|
||||
}
|
||||
}
|
||||
send0(p);
|
||||
}
|
||||
|
||||
protected abstract void send0(DatagramPacket p) throws IOException;
|
||||
|
||||
/**
|
||||
* Connects a datagram socket to a remote destination. This associates the remote
|
||||
* address with the local socket so that datagrams may only be sent to this destination
|
||||
* and received from this destination.
|
||||
* @param address the remote InetAddress to connect to
|
||||
* @param port the remote port number
|
||||
*/
|
||||
protected void connect(InetAddress address, int port) throws SocketException {
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
connect0(address, port);
|
||||
connectedAddress = address;
|
||||
connectedPort = port;
|
||||
connected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects a previously connected socket. Does nothing if the socket was
|
||||
* not connected already.
|
||||
*/
|
||||
protected void disconnect() {
|
||||
disconnect0(connectedAddress.holder().getFamily());
|
||||
connected = false;
|
||||
connectedAddress = null;
|
||||
connectedPort = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek at the packet to see who it is from.
|
||||
* @param i the address to populate with the sender address
|
||||
*/
|
||||
protected abstract int peek(InetAddress i) throws IOException;
|
||||
protected abstract int peekData(DatagramPacket p) throws IOException;
|
||||
/**
|
||||
* Receive the datagram packet.
|
||||
* @param p the packet to receive into
|
||||
*/
|
||||
protected synchronized void receive(DatagramPacket p)
|
||||
throws IOException {
|
||||
receive0(p);
|
||||
}
|
||||
|
||||
protected abstract void receive0(DatagramPacket p)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Set the TTL (time-to-live) option.
|
||||
* @param ttl TTL to be set.
|
||||
*/
|
||||
protected abstract void setTimeToLive(int ttl) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the TTL (time-to-live) option.
|
||||
*/
|
||||
protected abstract int getTimeToLive() throws IOException;
|
||||
|
||||
/**
|
||||
* Set the TTL (time-to-live) option.
|
||||
* @param ttl TTL to be set.
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract void setTTL(byte ttl) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the TTL (time-to-live) option.
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract byte getTTL() throws IOException;
|
||||
|
||||
/**
|
||||
* Join the multicast group.
|
||||
* @param inetaddr multicast address to join.
|
||||
*/
|
||||
protected void join(InetAddress inetaddr) throws IOException {
|
||||
join(inetaddr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave the multicast group.
|
||||
* @param inetaddr multicast address to leave.
|
||||
*/
|
||||
protected void leave(InetAddress inetaddr) throws IOException {
|
||||
leave(inetaddr, null);
|
||||
}
|
||||
/**
|
||||
* Join the multicast group.
|
||||
* @param mcastaddr multicast address to join.
|
||||
* @param netIf specifies the local interface to receive multicast
|
||||
* datagram packets
|
||||
* @throws IllegalArgumentException if mcastaddr is null or is a
|
||||
* SocketAddress subclass not supported by this socket
|
||||
* @since 1.4
|
||||
*/
|
||||
|
||||
protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
|
||||
throws IOException {
|
||||
if (!(mcastaddr instanceof InetSocketAddress addr))
|
||||
throw new IllegalArgumentException("Unsupported address type");
|
||||
join(addr.getAddress(), netIf);
|
||||
}
|
||||
|
||||
protected abstract void join(InetAddress inetaddr, NetworkInterface netIf)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Leave the multicast group.
|
||||
* @param mcastaddr multicast address to leave.
|
||||
* @param netIf specified the local interface to leave the group at
|
||||
* @throws IllegalArgumentException if mcastaddr is null or is a
|
||||
* SocketAddress subclass not supported by this socket
|
||||
* @since 1.4
|
||||
*/
|
||||
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
|
||||
throws IOException {
|
||||
if (!(mcastaddr instanceof InetSocketAddress addr))
|
||||
throw new IllegalArgumentException("Unsupported address type");
|
||||
leave(addr.getAddress(), netIf);
|
||||
}
|
||||
|
||||
protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Close the socket.
|
||||
*/
|
||||
protected void close() {
|
||||
if (fd != null) {
|
||||
SocketCleanable.unregister(fd);
|
||||
datagramSocketClose();
|
||||
ResourceManager.afterUdpClose();
|
||||
fd = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isClosed() {
|
||||
return (fd == null) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* set a value - since we only support (setting) binary options
|
||||
* here, o must be a Boolean
|
||||
*/
|
||||
|
||||
public void setOption(int optID, Object o) throws SocketException {
|
||||
if (isClosed()) {
|
||||
throw new SocketException("Socket Closed");
|
||||
}
|
||||
switch (optID) {
|
||||
/* check type safety b4 going native. These should never
|
||||
* fail, since only java.Socket* has access to
|
||||
* PlainSocketImpl.setOption().
|
||||
*/
|
||||
case SO_TIMEOUT:
|
||||
if (!(o instanceof Integer)) {
|
||||
throw new SocketException("bad argument for SO_TIMEOUT");
|
||||
}
|
||||
int tmp = ((Integer) o).intValue();
|
||||
if (tmp < 0)
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
timeout = tmp;
|
||||
return;
|
||||
case IP_TOS:
|
||||
if (!(o instanceof Integer)) {
|
||||
throw new SocketException("bad argument for IP_TOS");
|
||||
}
|
||||
trafficClass = ((Integer)o).intValue();
|
||||
break;
|
||||
case SO_REUSEADDR:
|
||||
if (!(o instanceof Boolean)) {
|
||||
throw new SocketException("bad argument for SO_REUSEADDR");
|
||||
}
|
||||
break;
|
||||
case SO_BROADCAST:
|
||||
if (!(o instanceof Boolean)) {
|
||||
throw new SocketException("bad argument for SO_BROADCAST");
|
||||
}
|
||||
break;
|
||||
case SO_BINDADDR:
|
||||
throw new SocketException("Cannot re-bind Socket");
|
||||
case SO_RCVBUF:
|
||||
case SO_SNDBUF:
|
||||
if (!(o instanceof Integer) ||
|
||||
((Integer)o).intValue() < 0) {
|
||||
throw new SocketException("bad argument for SO_SNDBUF or " +
|
||||
"SO_RCVBUF");
|
||||
}
|
||||
break;
|
||||
case IP_MULTICAST_IF:
|
||||
if (!(o instanceof InetAddress))
|
||||
throw new SocketException("bad argument for IP_MULTICAST_IF");
|
||||
break;
|
||||
case IP_MULTICAST_IF2:
|
||||
if (!(o instanceof NetworkInterface))
|
||||
throw new SocketException("bad argument for IP_MULTICAST_IF2");
|
||||
break;
|
||||
case IP_MULTICAST_LOOP:
|
||||
if (!(o instanceof Boolean))
|
||||
throw new SocketException("bad argument for IP_MULTICAST_LOOP");
|
||||
break;
|
||||
case SO_REUSEPORT:
|
||||
if (!(o instanceof Boolean)) {
|
||||
throw new SocketException("bad argument for SO_REUSEPORT");
|
||||
}
|
||||
if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new SocketException("invalid option: " + optID);
|
||||
}
|
||||
socketSetOption(optID, o);
|
||||
}
|
||||
|
||||
/*
|
||||
* get option's state - set or not
|
||||
*/
|
||||
|
||||
public Object getOption(int optID) throws SocketException {
|
||||
if (isClosed()) {
|
||||
throw new SocketException("Socket Closed");
|
||||
}
|
||||
|
||||
Object result;
|
||||
|
||||
switch (optID) {
|
||||
case SO_TIMEOUT:
|
||||
result = timeout;
|
||||
break;
|
||||
|
||||
case IP_TOS:
|
||||
result = socketGetOption(optID);
|
||||
if ( ((Integer)result).intValue() == -1) {
|
||||
result = trafficClass;
|
||||
}
|
||||
break;
|
||||
|
||||
case SO_BINDADDR:
|
||||
case IP_MULTICAST_IF:
|
||||
case IP_MULTICAST_IF2:
|
||||
case SO_RCVBUF:
|
||||
case SO_SNDBUF:
|
||||
case IP_MULTICAST_LOOP:
|
||||
case SO_REUSEADDR:
|
||||
case SO_BROADCAST:
|
||||
result = socketGetOption(optID);
|
||||
break;
|
||||
|
||||
case SO_REUSEPORT:
|
||||
if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
}
|
||||
result = socketGetOption(optID);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new SocketException("invalid option: " + optID);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static final ExtendedSocketOptions extendedOptions =
|
||||
ExtendedSocketOptions.getInstance();
|
||||
|
||||
private static final Set<SocketOption<?>> datagramSocketOptions = datagramSocketOptions();
|
||||
|
||||
private static Set<SocketOption<?>> datagramSocketOptions() {
|
||||
HashSet<SocketOption<?>> options = new HashSet<>();
|
||||
options.add(StandardSocketOptions.SO_SNDBUF);
|
||||
options.add(StandardSocketOptions.SO_RCVBUF);
|
||||
options.add(StandardSocketOptions.SO_REUSEADDR);
|
||||
options.add(StandardSocketOptions.SO_BROADCAST);
|
||||
options.add(StandardSocketOptions.IP_TOS);
|
||||
options.add(StandardSocketOptions.IP_MULTICAST_IF);
|
||||
options.add(StandardSocketOptions.IP_MULTICAST_TTL);
|
||||
options.add(StandardSocketOptions.IP_MULTICAST_LOOP);
|
||||
if (isReusePortAvailable())
|
||||
options.add(StandardSocketOptions.SO_REUSEPORT);
|
||||
options.addAll(ExtendedSocketOptions.datagramSocketOptions());
|
||||
return Collections.unmodifiableSet(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<SocketOption<?>> supportedOptions() {
|
||||
return datagramSocketOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
if (!name.type().isInstance(value))
|
||||
throw new IllegalArgumentException("Invalid value '" + value + "'");
|
||||
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket closed");
|
||||
|
||||
if (name == StandardSocketOptions.SO_SNDBUF) {
|
||||
if (((Integer)value).intValue() < 0)
|
||||
throw new IllegalArgumentException("Invalid send buffer size:" + value);
|
||||
setOption(SocketOptions.SO_SNDBUF, value);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
if (((Integer)value).intValue() < 0)
|
||||
throw new IllegalArgumentException("Invalid recv buffer size:" + value);
|
||||
setOption(SocketOptions.SO_RCVBUF, value);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
|
||||
setOption(SocketOptions.SO_REUSEADDR, value);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEPORT) {
|
||||
setOption(SocketOptions.SO_REUSEPORT, value);
|
||||
} else if (name == StandardSocketOptions.SO_BROADCAST) {
|
||||
setOption(SocketOptions.SO_BROADCAST, value);
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
int i = ((Integer)value).intValue();
|
||||
if (i < 0 || i > 255)
|
||||
throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
|
||||
setOption(SocketOptions.IP_TOS, value);
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_IF ) {
|
||||
setOption(SocketOptions.IP_MULTICAST_IF2, value);
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
|
||||
int i = ((Integer)value).intValue();
|
||||
if (i < 0 || i > 255)
|
||||
throw new IllegalArgumentException("Invalid TTL/hop value: " + value);
|
||||
setTimeToLive((Integer)value);
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
|
||||
boolean enable = (boolean) value;
|
||||
// Legacy setOption expects true to mean 'disabled'
|
||||
setOption(SocketOptions.IP_MULTICAST_LOOP, !enable);
|
||||
} else if (extendedOptions.isOptionSupported(name)) {
|
||||
extendedOptions.setOption(fd, name, value);
|
||||
} else {
|
||||
throw new AssertionError("unknown option :" + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket closed");
|
||||
|
||||
if (name == StandardSocketOptions.SO_SNDBUF) {
|
||||
return (T) getOption(SocketOptions.SO_SNDBUF);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
return (T) getOption(SocketOptions.SO_RCVBUF);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
|
||||
return (T) getOption(SocketOptions.SO_REUSEADDR);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEPORT) {
|
||||
return (T) getOption(SocketOptions.SO_REUSEPORT);
|
||||
} else if (name == StandardSocketOptions.SO_BROADCAST) {
|
||||
return (T) getOption(SocketOptions.SO_BROADCAST);
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
return (T) getOption(SocketOptions.IP_TOS);
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_IF) {
|
||||
return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
|
||||
return (T) ((Integer) getTimeToLive());
|
||||
} else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
|
||||
boolean disabled = (boolean) getOption(SocketOptions.IP_MULTICAST_LOOP);
|
||||
// Legacy getOption returns true when disabled
|
||||
return (T) Boolean.valueOf(!disabled);
|
||||
} else if (extendedOptions.isOptionSupported(name)) {
|
||||
return (T) extendedOptions.getOption(fd, name);
|
||||
} else {
|
||||
throw new AssertionError("unknown option: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void datagramSocketCreate() throws SocketException;
|
||||
protected abstract void datagramSocketClose();
|
||||
protected abstract void socketSetOption(int opt, Object val)
|
||||
throws SocketException;
|
||||
protected abstract Object socketGetOption(int opt) throws SocketException;
|
||||
|
||||
protected abstract void connect0(InetAddress address, int port) throws SocketException;
|
||||
protected abstract void disconnect0(int family);
|
||||
|
||||
protected boolean nativeConnectDisabled() {
|
||||
return connectDisabled;
|
||||
}
|
||||
|
||||
abstract int dataAvailable();
|
||||
private static native boolean isReusePortAvailable0();
|
||||
}
|
|
@ -1,875 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, 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 java.net;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.net.ConnectionResetException;
|
||||
import sun.net.NetHooks;
|
||||
import sun.net.PlatformSocketImpl;
|
||||
import sun.net.ResourceManager;
|
||||
import sun.net.ext.ExtendedSocketOptions;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.net.util.SocketExceptions;
|
||||
|
||||
/**
|
||||
* Default Socket Implementation. This implementation does
|
||||
* not implement any security checks.
|
||||
* Note this class should <b>NOT</b> be public.
|
||||
*
|
||||
* @author Steven B. Byrne
|
||||
*/
|
||||
abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {
|
||||
/* instance variable for SO_TIMEOUT */
|
||||
int timeout; // timeout in millisec
|
||||
// traffic class
|
||||
private int trafficClass;
|
||||
|
||||
private boolean shut_rd = false;
|
||||
private boolean shut_wr = false;
|
||||
|
||||
private SocketInputStream socketInputStream = null;
|
||||
private SocketOutputStream socketOutputStream = null;
|
||||
|
||||
/* number of threads using the FileDescriptor */
|
||||
protected int fdUseCount = 0;
|
||||
|
||||
/* lock when increment/decrementing fdUseCount */
|
||||
protected final Object fdLock = new Object();
|
||||
|
||||
/* indicates a close is pending on the file descriptor */
|
||||
protected boolean closePending = false;
|
||||
|
||||
/* indicates connection reset state */
|
||||
private volatile boolean connectionReset;
|
||||
|
||||
/* indicates whether impl is bound */
|
||||
boolean isBound;
|
||||
|
||||
/* indicates whether impl is connected */
|
||||
volatile boolean isConnected;
|
||||
|
||||
/* whether this Socket is a stream (TCP) socket or not (UDP)
|
||||
*/
|
||||
protected boolean stream;
|
||||
|
||||
/* whether this is a server or not */
|
||||
final boolean isServer;
|
||||
|
||||
/**
|
||||
* Load net library into runtime.
|
||||
*/
|
||||
static {
|
||||
jdk.internal.loader.BootLoader.loadLibrary("net");
|
||||
}
|
||||
|
||||
private static volatile boolean checkedReusePort;
|
||||
private static volatile boolean isReusePortAvailable;
|
||||
|
||||
/**
|
||||
* Tells whether SO_REUSEPORT is supported.
|
||||
*/
|
||||
static boolean isReusePortAvailable() {
|
||||
if (!checkedReusePort) {
|
||||
isReusePortAvailable = isReusePortAvailable0();
|
||||
checkedReusePort = true;
|
||||
}
|
||||
return isReusePortAvailable;
|
||||
}
|
||||
|
||||
AbstractPlainSocketImpl(boolean isServer) {
|
||||
this.isServer = isServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket with a boolean that specifies whether this
|
||||
* is a stream socket (true) or an unconnected UDP socket (false).
|
||||
*/
|
||||
protected synchronized void create(boolean stream) throws IOException {
|
||||
this.stream = stream;
|
||||
if (!stream) {
|
||||
ResourceManager.beforeUdpCreate();
|
||||
// only create the fd after we know we will be able to create the socket
|
||||
fd = new FileDescriptor();
|
||||
try {
|
||||
socketCreate(false);
|
||||
SocketCleanable.register(fd, false);
|
||||
} catch (IOException ioe) {
|
||||
ResourceManager.afterUdpClose();
|
||||
fd = null;
|
||||
throw ioe;
|
||||
}
|
||||
} else {
|
||||
fd = new FileDescriptor();
|
||||
socketCreate(true);
|
||||
SocketCleanable.register(fd, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket and connects it to the specified port on
|
||||
* the specified host.
|
||||
* @param host the specified host
|
||||
* @param port the specified port
|
||||
*/
|
||||
protected void connect(String host, int port)
|
||||
throws UnknownHostException, IOException
|
||||
{
|
||||
boolean connected = false;
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(host);
|
||||
// recording this.address as supplied by caller before calling connect
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
|
||||
connectToAddress(address, port, timeout);
|
||||
connected = true;
|
||||
} finally {
|
||||
if (!connected) {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ioe) {
|
||||
/* Do nothing. If connect threw an exception then
|
||||
it will be passed up the call stack */
|
||||
}
|
||||
}
|
||||
isConnected = connected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket and connects it to the specified address on
|
||||
* the specified port.
|
||||
* @param address the address
|
||||
* @param port the specified port
|
||||
*/
|
||||
protected void connect(InetAddress address, int port) throws IOException {
|
||||
// recording this.address as supplied by caller before calling connect
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
|
||||
try {
|
||||
connectToAddress(address, port, timeout);
|
||||
isConnected = true;
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
// everything failed
|
||||
close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket and connects it to the specified address on
|
||||
* the specified port.
|
||||
* @param address the address
|
||||
* @param timeout the timeout value in milliseconds, or zero for no timeout.
|
||||
* @throws IOException if connection fails
|
||||
* @throws IllegalArgumentException if address is null or is a
|
||||
* SocketAddress subclass not supported by this socket
|
||||
* @since 1.4
|
||||
*/
|
||||
protected void connect(SocketAddress address, int timeout)
|
||||
throws IOException {
|
||||
boolean connected = false;
|
||||
try {
|
||||
if (!(address instanceof InetSocketAddress addr))
|
||||
throw new IllegalArgumentException("unsupported address type");
|
||||
if (addr.isUnresolved())
|
||||
throw new UnknownHostException(addr.getHostName());
|
||||
// recording this.address as supplied by caller before calling connect
|
||||
InetAddress ia = addr.getAddress();
|
||||
this.address = ia;
|
||||
this.port = addr.getPort();
|
||||
if (ia.isLinkLocalAddress()) {
|
||||
ia = IPAddressUtil.toScopedAddress(ia);
|
||||
}
|
||||
connectToAddress(ia, port, timeout);
|
||||
connected = true;
|
||||
} finally {
|
||||
if (!connected) {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ioe) {
|
||||
/* Do nothing. If connect threw an exception then
|
||||
it will be passed up the call stack */
|
||||
}
|
||||
}
|
||||
isConnected = connected;
|
||||
}
|
||||
}
|
||||
|
||||
private void connectToAddress(InetAddress address, int port, int timeout) throws IOException {
|
||||
if (address.isAnyLocalAddress()) {
|
||||
doConnect(InetAddress.getLocalHost(), port, timeout);
|
||||
} else {
|
||||
doConnect(address, port, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOption(int opt, Object val) throws SocketException {
|
||||
if (isClosedOrPending()) {
|
||||
throw new SocketException("Socket Closed");
|
||||
}
|
||||
boolean on = true;
|
||||
switch (opt) {
|
||||
/* check type safety b4 going native. These should never
|
||||
* fail, since only java.Socket* has access to
|
||||
* PlainSocketImpl.setOption().
|
||||
*/
|
||||
case SO_LINGER:
|
||||
if (!(val instanceof Integer) && !(val instanceof Boolean))
|
||||
throw new SocketException("Bad parameter for option");
|
||||
if (val instanceof Boolean) {
|
||||
/* true only if disabling - enabling should be Integer */
|
||||
on = false;
|
||||
}
|
||||
break;
|
||||
case SO_TIMEOUT:
|
||||
if (!(val instanceof Integer))
|
||||
throw new SocketException("Bad parameter for SO_TIMEOUT");
|
||||
int tmp = ((Integer) val).intValue();
|
||||
if (tmp < 0)
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
timeout = tmp;
|
||||
break;
|
||||
case IP_TOS:
|
||||
if (!(val instanceof Integer)) {
|
||||
throw new SocketException("bad argument for IP_TOS");
|
||||
}
|
||||
trafficClass = ((Integer)val).intValue();
|
||||
break;
|
||||
case SO_BINDADDR:
|
||||
throw new SocketException("Cannot re-bind socket");
|
||||
case TCP_NODELAY:
|
||||
if (!(val instanceof Boolean))
|
||||
throw new SocketException("bad parameter for TCP_NODELAY");
|
||||
on = ((Boolean)val).booleanValue();
|
||||
break;
|
||||
case SO_SNDBUF:
|
||||
case SO_RCVBUF:
|
||||
if (!(val instanceof Integer) ||
|
||||
!(((Integer)val).intValue() > 0)) {
|
||||
throw new SocketException("bad parameter for SO_SNDBUF " +
|
||||
"or SO_RCVBUF");
|
||||
}
|
||||
break;
|
||||
case SO_KEEPALIVE:
|
||||
if (!(val instanceof Boolean))
|
||||
throw new SocketException("bad parameter for SO_KEEPALIVE");
|
||||
on = ((Boolean)val).booleanValue();
|
||||
break;
|
||||
case SO_OOBINLINE:
|
||||
if (!(val instanceof Boolean))
|
||||
throw new SocketException("bad parameter for SO_OOBINLINE");
|
||||
on = ((Boolean)val).booleanValue();
|
||||
break;
|
||||
case SO_REUSEADDR:
|
||||
if (!(val instanceof Boolean))
|
||||
throw new SocketException("bad parameter for SO_REUSEADDR");
|
||||
on = ((Boolean)val).booleanValue();
|
||||
break;
|
||||
case SO_REUSEPORT:
|
||||
if (!(val instanceof Boolean))
|
||||
throw new SocketException("bad parameter for SO_REUSEPORT");
|
||||
if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT))
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
on = ((Boolean)val).booleanValue();
|
||||
break;
|
||||
default:
|
||||
throw new SocketException("unrecognized TCP option: " + opt);
|
||||
}
|
||||
socketSetOption(opt, on, val);
|
||||
}
|
||||
public Object getOption(int opt) throws SocketException {
|
||||
if (isClosedOrPending()) {
|
||||
throw new SocketException("Socket Closed");
|
||||
}
|
||||
if (opt == SO_TIMEOUT) {
|
||||
return timeout;
|
||||
}
|
||||
int ret = 0;
|
||||
/*
|
||||
* The native socketGetOption() knows about 3 options.
|
||||
* The 32 bit value it returns will be interpreted according
|
||||
* to what we're asking. A return of -1 means it understands
|
||||
* the option but its turned off. It will raise a SocketException
|
||||
* if "opt" isn't one it understands.
|
||||
*/
|
||||
|
||||
switch (opt) {
|
||||
case TCP_NODELAY:
|
||||
ret = socketGetOption(opt, null);
|
||||
return Boolean.valueOf(ret != -1);
|
||||
case SO_OOBINLINE:
|
||||
ret = socketGetOption(opt, null);
|
||||
return Boolean.valueOf(ret != -1);
|
||||
case SO_LINGER:
|
||||
ret = socketGetOption(opt, null);
|
||||
return (ret == -1) ? Boolean.FALSE: (Object)(ret);
|
||||
case SO_REUSEADDR:
|
||||
ret = socketGetOption(opt, null);
|
||||
return Boolean.valueOf(ret != -1);
|
||||
case SO_BINDADDR:
|
||||
InetAddressContainer in = new InetAddressContainer();
|
||||
ret = socketGetOption(opt, in);
|
||||
return in.addr;
|
||||
case SO_SNDBUF:
|
||||
case SO_RCVBUF:
|
||||
ret = socketGetOption(opt, null);
|
||||
return ret;
|
||||
case IP_TOS:
|
||||
try {
|
||||
ret = socketGetOption(opt, null);
|
||||
if (ret == -1) { // ipv6 tos
|
||||
return trafficClass;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
} catch (SocketException se) {
|
||||
// TODO - should make better effort to read TOS or TCLASS
|
||||
return trafficClass; // ipv6 tos
|
||||
}
|
||||
case SO_KEEPALIVE:
|
||||
ret = socketGetOption(opt, null);
|
||||
return Boolean.valueOf(ret != -1);
|
||||
case SO_REUSEPORT:
|
||||
if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
}
|
||||
ret = socketGetOption(opt, null);
|
||||
return Boolean.valueOf(ret != -1);
|
||||
// should never get here
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final ExtendedSocketOptions extendedOptions =
|
||||
ExtendedSocketOptions.getInstance();
|
||||
|
||||
private static final Set<SocketOption<?>> clientSocketOptions = clientSocketOptions();
|
||||
private static final Set<SocketOption<?>> serverSocketOptions = serverSocketOptions();
|
||||
|
||||
private static Set<SocketOption<?>> clientSocketOptions() {
|
||||
HashSet<SocketOption<?>> options = new HashSet<>();
|
||||
options.add(StandardSocketOptions.SO_KEEPALIVE);
|
||||
options.add(StandardSocketOptions.SO_SNDBUF);
|
||||
options.add(StandardSocketOptions.SO_RCVBUF);
|
||||
options.add(StandardSocketOptions.SO_REUSEADDR);
|
||||
options.add(StandardSocketOptions.SO_LINGER);
|
||||
options.add(StandardSocketOptions.IP_TOS);
|
||||
options.add(StandardSocketOptions.TCP_NODELAY);
|
||||
if (isReusePortAvailable())
|
||||
options.add(StandardSocketOptions.SO_REUSEPORT);
|
||||
options.addAll(ExtendedSocketOptions.clientSocketOptions());
|
||||
return Collections.unmodifiableSet(options);
|
||||
}
|
||||
|
||||
private static Set<SocketOption<?>> serverSocketOptions() {
|
||||
HashSet<SocketOption<?>> options = new HashSet<>();
|
||||
options.add(StandardSocketOptions.SO_RCVBUF);
|
||||
options.add(StandardSocketOptions.SO_REUSEADDR);
|
||||
options.add(StandardSocketOptions.IP_TOS);
|
||||
if (isReusePortAvailable())
|
||||
options.add(StandardSocketOptions.SO_REUSEPORT);
|
||||
options.addAll(ExtendedSocketOptions.serverSocketOptions());
|
||||
return Collections.unmodifiableSet(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<SocketOption<?>> supportedOptions() {
|
||||
if (isServer)
|
||||
return serverSocketOptions;
|
||||
else
|
||||
return clientSocketOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
if (!name.type().isInstance(value))
|
||||
throw new IllegalArgumentException("Invalid value '" + value + "'");
|
||||
|
||||
if (isClosedOrPending())
|
||||
throw new SocketException("Socket closed");
|
||||
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE) {
|
||||
setOption(SocketOptions.SO_KEEPALIVE, value);
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF) {
|
||||
if (((Integer)value).intValue() < 0)
|
||||
throw new IllegalArgumentException("Invalid send buffer size:" + value);
|
||||
setOption(SocketOptions.SO_SNDBUF, value);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
if (((Integer)value).intValue() < 0)
|
||||
throw new IllegalArgumentException("Invalid recv buffer size:" + value);
|
||||
setOption(SocketOptions.SO_RCVBUF, value);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
|
||||
setOption(SocketOptions.SO_REUSEADDR, value);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEPORT) {
|
||||
setOption(SocketOptions.SO_REUSEPORT, value);
|
||||
} else if (name == StandardSocketOptions.SO_LINGER ) {
|
||||
if (((Integer)value).intValue() < 0)
|
||||
setOption(SocketOptions.SO_LINGER, false);
|
||||
else
|
||||
setOption(SocketOptions.SO_LINGER, value);
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
int i = ((Integer)value).intValue();
|
||||
if (i < 0 || i > 255)
|
||||
throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
|
||||
setOption(SocketOptions.IP_TOS, value);
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY) {
|
||||
setOption(SocketOptions.TCP_NODELAY, value);
|
||||
} else if (extendedOptions.isOptionSupported(name)) {
|
||||
extendedOptions.setOption(fd, name, value);
|
||||
} else {
|
||||
throw new AssertionError("unknown option: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
if (isClosedOrPending())
|
||||
throw new SocketException("Socket closed");
|
||||
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE) {
|
||||
return (T)getOption(SocketOptions.SO_KEEPALIVE);
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF) {
|
||||
return (T)getOption(SocketOptions.SO_SNDBUF);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
return (T)getOption(SocketOptions.SO_RCVBUF);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
|
||||
return (T)getOption(SocketOptions.SO_REUSEADDR);
|
||||
} else if (name == StandardSocketOptions.SO_REUSEPORT) {
|
||||
return (T)getOption(SocketOptions.SO_REUSEPORT);
|
||||
} else if (name == StandardSocketOptions.SO_LINGER) {
|
||||
Object value = getOption(SocketOptions.SO_LINGER);
|
||||
if (value instanceof Boolean) {
|
||||
assert ((Boolean)value).booleanValue() == false;
|
||||
value = -1;
|
||||
}
|
||||
return (T)value;
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
return (T)getOption(SocketOptions.IP_TOS);
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY) {
|
||||
return (T)getOption(SocketOptions.TCP_NODELAY);
|
||||
} else if (extendedOptions.isOptionSupported(name)) {
|
||||
return (T) extendedOptions.getOption(fd, name);
|
||||
} else {
|
||||
throw new AssertionError("unknown option: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The workhorse of the connection operation. Tries several times to
|
||||
* establish a connection to the given <host, port>. If unsuccessful,
|
||||
* throws an IOException indicating what went wrong.
|
||||
*/
|
||||
|
||||
synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
|
||||
synchronized (fdLock) {
|
||||
if (!closePending && !isBound) {
|
||||
NetHooks.beforeTcpConnect(fd, address, port);
|
||||
}
|
||||
}
|
||||
try {
|
||||
acquireFD();
|
||||
try {
|
||||
socketConnect(address, port, timeout);
|
||||
/* socket may have been closed during poll/select */
|
||||
synchronized (fdLock) {
|
||||
if (closePending) {
|
||||
throw new SocketException ("Socket closed");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
releaseFD();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
close();
|
||||
throw SocketExceptions.of(e, new InetSocketAddress(address, port));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the socket to the specified address of the specified local port.
|
||||
* @param address the address
|
||||
* @param lport the port
|
||||
*/
|
||||
protected synchronized void bind(InetAddress address, int lport)
|
||||
throws IOException
|
||||
{
|
||||
synchronized (fdLock) {
|
||||
if (!closePending && !isBound) {
|
||||
NetHooks.beforeTcpBind(fd, address, lport);
|
||||
}
|
||||
}
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
socketBind(address, lport);
|
||||
isBound = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens, for a specified amount of time, for connections.
|
||||
* @param count the amount of time to listen for connections
|
||||
*/
|
||||
protected synchronized void listen(int count) throws IOException {
|
||||
socketListen(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts connections.
|
||||
* @param si the socket impl
|
||||
*/
|
||||
protected void accept(SocketImpl si) throws IOException {
|
||||
si.fd = new FileDescriptor();
|
||||
acquireFD();
|
||||
try {
|
||||
socketAccept(si);
|
||||
} finally {
|
||||
releaseFD();
|
||||
}
|
||||
SocketCleanable.register(si.fd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an InputStream for this socket.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
protected synchronized InputStream getInputStream() throws IOException {
|
||||
synchronized (fdLock) {
|
||||
if (isClosedOrPending())
|
||||
throw new IOException("Socket Closed");
|
||||
if (shut_rd)
|
||||
throw new IOException("Socket input is shutdown");
|
||||
if (socketInputStream == null) {
|
||||
PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);
|
||||
try {
|
||||
socketInputStream = AccessController.doPrivileged(pa);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
return socketInputStream;
|
||||
}
|
||||
|
||||
void setInputStream(SocketInputStream in) {
|
||||
socketInputStream = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an OutputStream for this socket.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
protected synchronized OutputStream getOutputStream() throws IOException {
|
||||
synchronized (fdLock) {
|
||||
if (isClosedOrPending())
|
||||
throw new IOException("Socket Closed");
|
||||
if (shut_wr)
|
||||
throw new IOException("Socket output is shutdown");
|
||||
if (socketOutputStream == null) {
|
||||
PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);
|
||||
try {
|
||||
socketOutputStream = AccessController.doPrivileged(pa);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
return socketOutputStream;
|
||||
}
|
||||
|
||||
void setFileDescriptor(FileDescriptor fd) {
|
||||
this.fd = fd;
|
||||
}
|
||||
|
||||
void setAddress(InetAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
void setLocalPort(int localport) {
|
||||
this.localport = localport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes that can be read without blocking.
|
||||
*/
|
||||
protected synchronized int available() throws IOException {
|
||||
if (isClosedOrPending()) {
|
||||
throw new IOException("Stream closed.");
|
||||
}
|
||||
|
||||
/*
|
||||
* If connection has been reset or shut down for input, then return 0
|
||||
* to indicate there are no buffered bytes.
|
||||
*/
|
||||
if (isConnectionReset() || shut_rd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no bytes available and we were previously notified
|
||||
* of a connection reset then we move to the reset state.
|
||||
*
|
||||
* If are notified of a connection reset then check
|
||||
* again if there are bytes buffered on the socket.
|
||||
*/
|
||||
int n = 0;
|
||||
try {
|
||||
n = socketAvailable();
|
||||
} catch (ConnectionResetException exc1) {
|
||||
setConnectionReset();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the socket.
|
||||
*/
|
||||
protected void close() throws IOException {
|
||||
synchronized(fdLock) {
|
||||
if (fd != null) {
|
||||
if (fdUseCount == 0) {
|
||||
if (closePending) {
|
||||
return;
|
||||
}
|
||||
closePending = true;
|
||||
/*
|
||||
* We close the FileDescriptor in two-steps - first the
|
||||
* "pre-close" which closes the socket but doesn't
|
||||
* release the underlying file descriptor. This operation
|
||||
* may be lengthy due to untransmitted data and a long
|
||||
* linger interval. Once the pre-close is done we do the
|
||||
* actual socket to release the fd.
|
||||
*/
|
||||
try {
|
||||
socketPreClose();
|
||||
} finally {
|
||||
socketClose();
|
||||
}
|
||||
fd = null;
|
||||
return;
|
||||
} else {
|
||||
/*
|
||||
* If a thread has acquired the fd and a close
|
||||
* isn't pending then use a deferred close.
|
||||
* Also decrement fdUseCount to signal the last
|
||||
* thread that releases the fd to close it.
|
||||
*/
|
||||
if (!closePending) {
|
||||
closePending = true;
|
||||
fdUseCount--;
|
||||
socketPreClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
throw new InternalError("should not get here");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown read-half of the socket connection;
|
||||
*/
|
||||
protected void shutdownInput() throws IOException {
|
||||
if (fd != null) {
|
||||
socketShutdown(SHUT_RD);
|
||||
if (socketInputStream != null) {
|
||||
socketInputStream.setEOF(true);
|
||||
}
|
||||
shut_rd = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown write-half of the socket connection;
|
||||
*/
|
||||
protected void shutdownOutput() throws IOException {
|
||||
if (fd != null) {
|
||||
socketShutdown(SHUT_WR);
|
||||
shut_wr = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean supportsUrgentData () {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void sendUrgentData (int data) throws IOException {
|
||||
if (fd == null) {
|
||||
throw new IOException("Socket Closed");
|
||||
}
|
||||
socketSendUrgentData (data);
|
||||
}
|
||||
|
||||
/*
|
||||
* "Acquires" and returns the FileDescriptor for this impl
|
||||
*
|
||||
* A corresponding releaseFD is required to "release" the
|
||||
* FileDescriptor.
|
||||
*/
|
||||
FileDescriptor acquireFD() {
|
||||
synchronized (fdLock) {
|
||||
fdUseCount++;
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "Release" the FileDescriptor for this impl.
|
||||
*
|
||||
* If the use count goes to -1 then the socket is closed.
|
||||
*/
|
||||
void releaseFD() {
|
||||
synchronized (fdLock) {
|
||||
fdUseCount--;
|
||||
if (fdUseCount == -1) {
|
||||
if (fd != null) {
|
||||
try {
|
||||
socketClose();
|
||||
} catch (IOException e) {
|
||||
} finally {
|
||||
fd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isConnectionReset() {
|
||||
return connectionReset;
|
||||
}
|
||||
|
||||
void setConnectionReset() {
|
||||
connectionReset = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if already closed or close is pending
|
||||
*/
|
||||
public boolean isClosedOrPending() {
|
||||
/*
|
||||
* Lock on fdLock to ensure that we wait if a
|
||||
* close is in progress.
|
||||
*/
|
||||
synchronized (fdLock) {
|
||||
if (closePending || (fd == null)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current value of SO_TIMEOUT
|
||||
*/
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* "Pre-close" a socket by dup'ing the file descriptor - this enables
|
||||
* the socket to be closed without releasing the file descriptor.
|
||||
*/
|
||||
private void socketPreClose() throws IOException {
|
||||
socketClose0(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the socket (and release the file descriptor).
|
||||
*/
|
||||
protected void socketClose() throws IOException {
|
||||
SocketCleanable.unregister(fd);
|
||||
try {
|
||||
socketClose0(false);
|
||||
} finally {
|
||||
if (!stream) {
|
||||
ResourceManager.afterUdpClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract void socketCreate(boolean stream) throws IOException;
|
||||
abstract void socketConnect(InetAddress address, int port, int timeout)
|
||||
throws IOException;
|
||||
abstract void socketBind(InetAddress address, int port)
|
||||
throws IOException;
|
||||
abstract void socketListen(int count)
|
||||
throws IOException;
|
||||
abstract void socketAccept(SocketImpl s)
|
||||
throws IOException;
|
||||
abstract int socketAvailable()
|
||||
throws IOException;
|
||||
abstract void socketClose0(boolean useDeferredClose)
|
||||
throws IOException;
|
||||
abstract void socketShutdown(int howto)
|
||||
throws IOException;
|
||||
abstract void socketSetOption(int cmd, boolean on, Object value)
|
||||
throws SocketException;
|
||||
abstract int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
|
||||
abstract void socketSendUrgentData(int data)
|
||||
throws IOException;
|
||||
|
||||
public static final int SHUT_RD = 0;
|
||||
public static final int SHUT_WR = 1;
|
||||
|
||||
private static native boolean isReusePortAvailable0();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2021, 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
|
||||
|
@ -52,14 +52,6 @@ package java.net;
|
|||
public final
|
||||
class DatagramPacket {
|
||||
|
||||
/**
|
||||
* Perform class initialization
|
||||
*/
|
||||
static {
|
||||
jdk.internal.loader.BootLoader.loadLibrary("net");
|
||||
init();
|
||||
}
|
||||
|
||||
/*
|
||||
* The fields of this class are package-private since DatagramSocketImpl
|
||||
* classes needs to access them.
|
||||
|
@ -424,9 +416,4 @@ class DatagramPacket {
|
|||
this.length = length;
|
||||
this.bufLength = this.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform class load-time initializations.
|
||||
*/
|
||||
private static native void init();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.nio.channels.DatagramChannel;
|
|||
import java.nio.channels.MulticastChannel;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import sun.net.NetProperties;
|
||||
import sun.nio.ch.DefaultSelectorProvider;
|
||||
|
@ -1338,14 +1339,6 @@ public class DatagramSocket implements java.io.Closeable {
|
|||
|
||||
// Temporary solution until JDK-8237352 is addressed
|
||||
private static final SocketAddress NO_DELEGATE = new SocketAddress() {};
|
||||
private static final boolean USE_PLAINDATAGRAMSOCKET = usePlainDatagramSocketImpl();
|
||||
|
||||
private static boolean usePlainDatagramSocketImpl() {
|
||||
PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
|
||||
@SuppressWarnings("removal")
|
||||
String s = AccessController.doPrivileged(pa);
|
||||
return (s != null) && (s.isEmpty() || s.equalsIgnoreCase("true"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Best effort to convert an {@link IOException}
|
||||
|
@ -1398,14 +1391,11 @@ public class DatagramSocket implements java.io.Closeable {
|
|||
boolean initialized = false;
|
||||
try {
|
||||
DatagramSocketImplFactory factory = DatagramSocket.factory;
|
||||
if (USE_PLAINDATAGRAMSOCKET || factory != null) {
|
||||
if (factory != null) {
|
||||
// create legacy DatagramSocket delegate
|
||||
DatagramSocketImpl impl;
|
||||
if (factory != null) {
|
||||
impl = factory.createDatagramSocketImpl();
|
||||
} else {
|
||||
impl = DefaultDatagramSocketImplFactory.createDatagramSocketImpl(multicast);
|
||||
}
|
||||
DatagramSocketImpl impl = factory.createDatagramSocketImpl();
|
||||
Objects.requireNonNull(impl,
|
||||
"Implementation returned by installed DatagramSocketImplFactory is null");
|
||||
delegate = new NetMulticastSocket(impl);
|
||||
((NetMulticastSocket) delegate).getImpl(); // ensure impl.create() is called.
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2021, 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
|
||||
|
@ -33,24 +33,6 @@ import java.util.Set;
|
|||
/**
|
||||
* Abstract datagram and multicast socket implementation base class.
|
||||
*
|
||||
* @implNote Sockets created with the {@code DatagramSocket} and {@code
|
||||
* MulticastSocket} public constructors historically delegated all socket
|
||||
* operations to a {@code DatagramSocketImpl} implementation named
|
||||
* "PlainDatagramSocketImpl". {@code DatagramSocket} and {@code MulticastSocket}
|
||||
* have since been changed to a new implementation based on {@code DatagramChannel}.
|
||||
* The JDK continues to ship with the older implementation to allow code to run
|
||||
* that depends on unspecified behavior that differs between the old and new
|
||||
* implementations. The old implementation will be used if the Java virtual
|
||||
* machine is started with the system property {@systemProperty
|
||||
* jdk.net.usePlainDatagramSocketImpl} set to use the old implementation. It may
|
||||
* also be set in the JDK's network configuration file, located in {@code
|
||||
* ${java.home}/conf/net.properties}. The value of the property is the string
|
||||
* representation of a boolean. If set without a value then it defaults to {@code
|
||||
* true}, hence running with {@code -Djdk.net.usePlainDatagramSocketImpl} or
|
||||
* {@code -Djdk.net.usePlainDatagramSocketImpl=true} will configure the Java
|
||||
* virtual machine to use the old implementation. The property and old
|
||||
* implementation will be removed in a future version.
|
||||
*
|
||||
* @author Pavani Diwanji
|
||||
* @since 1.1
|
||||
*/
|
||||
|
|
|
@ -136,8 +136,7 @@ final class NetMulticastSocket extends MulticastSocket {
|
|||
bind(new InetSocketAddress(0));
|
||||
|
||||
// old impls do not support connect/disconnect
|
||||
if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
|
||||
((AbstractPlainDatagramSocketImpl) impl).nativeConnectDisabled())) {
|
||||
if (oldImpl) {
|
||||
connectState = ST_CONNECTED_NO_IMPL;
|
||||
} else {
|
||||
try {
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 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 java.net;
|
||||
|
||||
import jdk.internal.access.JavaIOFileDescriptorAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import jdk.internal.ref.PhantomCleanable;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
|
||||
import sun.net.ResourceManager;
|
||||
|
||||
/**
|
||||
* Cleanable for a socket/datagramsocket FileDescriptor when it becomes phantom reachable.
|
||||
* Create a cleanup if the raw fd != -1. Windows closes sockets using the fd.
|
||||
* Subclassed from {@code PhantomCleanable} so that {@code clear} can be
|
||||
* called to disable the cleanup when the socket fd is closed by any means
|
||||
* other than calling {@link FileDescriptor#close}.
|
||||
* Otherwise, it might incorrectly close the handle or fd after it has been reused.
|
||||
*/
|
||||
final class SocketCleanable extends PhantomCleanable<FileDescriptor> {
|
||||
|
||||
// Access to FileDescriptor private fields
|
||||
private static final JavaIOFileDescriptorAccess fdAccess =
|
||||
SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||
|
||||
// Native function to call NET_SocketClose(fd)
|
||||
// Used only for last chance cleanup.
|
||||
private static native void cleanupClose0(int fd) throws IOException;
|
||||
|
||||
// The raw fd to close
|
||||
private final int fd;
|
||||
|
||||
// true for socket, false for datagram socket
|
||||
private final boolean stream;
|
||||
|
||||
/**
|
||||
* Register a socket specific Cleanable with the FileDescriptor
|
||||
* if the FileDescriptor is non-null and the raw fd is != -1.
|
||||
*
|
||||
* @param fdo the FileDescriptor; may be null
|
||||
* @param stream false for datagram socket
|
||||
*/
|
||||
static void register(FileDescriptor fdo, boolean stream) {
|
||||
if (fdo != null && fdo.valid()) {
|
||||
int fd = fdAccess.get(fdo);
|
||||
fdAccess.registerCleanup(fdo,
|
||||
new SocketCleanable(fdo, CleanerFactory.cleaner(),
|
||||
fd, stream));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a Cleanable from the FileDescriptor.
|
||||
* @param fdo the FileDescriptor; may be null
|
||||
*/
|
||||
static void unregister(FileDescriptor fdo) {
|
||||
if (fdo != null) {
|
||||
fdAccess.unregisterCleanup(fdo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a phantom cleanable reference.
|
||||
*
|
||||
* @param obj the object to monitor
|
||||
* @param cleaner the cleaner
|
||||
* @param fd file descriptor to close
|
||||
* @param stream false for datagram socket
|
||||
*/
|
||||
private SocketCleanable(FileDescriptor obj, Cleaner cleaner,
|
||||
int fd, boolean stream) {
|
||||
super(obj, cleaner);
|
||||
this.fd = fd;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the native handle or fd.
|
||||
*/
|
||||
@Override
|
||||
protected void performCleanup() {
|
||||
try {
|
||||
cleanupClose0(fd);
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException("close", ioe);
|
||||
} finally {
|
||||
if (!stream) {
|
||||
ResourceManager.afterUdpClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,12 +29,9 @@ import java.io.FileDescriptor;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.net.NetProperties;
|
||||
import sun.net.PlatformSocketImpl;
|
||||
import sun.nio.ch.NioSocketImpl;
|
||||
|
||||
|
@ -43,45 +40,16 @@ import sun.nio.ch.NioSocketImpl;
|
|||
* of all classes that actually implement sockets. It is used to
|
||||
* create both client and server sockets.
|
||||
*
|
||||
* @implNote Client and server sockets created with the {@code Socket} and
|
||||
* {@code SocketServer} public constructors create a system-default
|
||||
* {@code SocketImpl}. The JDK historically used a {@code SocketImpl}
|
||||
* implementation type named "PlainSocketImpl" that has since been replaced by a
|
||||
* newer implementation. The JDK continues to ship with the older implementation
|
||||
* to allow code to run that depends on unspecified behavior that differs between
|
||||
* the old and new implementations. The old implementation will be used if the
|
||||
* Java virtual machine is started with the system property {@systemProperty
|
||||
* jdk.net.usePlainSocketImpl} set to use the old implementation. It may also be
|
||||
* set in the JDK's network configuration file, located in {@code
|
||||
* ${java.home}/conf/net.properties}. The value of the property is the string
|
||||
* representation of a boolean. If set without a value then it defaults to {@code
|
||||
* true}, hence running with {@code -Djdk.net.usePlainSocketImpl} or {@code
|
||||
* -Djdk.net.usePlainSocketImpl=true} will configure the Java virtual machine
|
||||
* to use the old implementation. The property and old implementation will be
|
||||
* removed in a future version.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class SocketImpl implements SocketOptions {
|
||||
private static final boolean USE_PLAINSOCKETIMPL = usePlainSocketImpl();
|
||||
|
||||
private static boolean usePlainSocketImpl() {
|
||||
PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainSocketImpl");
|
||||
@SuppressWarnings("removal")
|
||||
String s = AccessController.doPrivileged(pa);
|
||||
return (s != null) && !s.equalsIgnoreCase("false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of platform's SocketImpl
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
|
||||
if (USE_PLAINSOCKETIMPL) {
|
||||
return (S) new PlainSocketImpl(server);
|
||||
} else {
|
||||
return (S) new NioSocketImpl(server);
|
||||
}
|
||||
return (S) new NioSocketImpl(server);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, 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 java.net;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import sun.net.ConnectionResetException;
|
||||
|
||||
/**
|
||||
* This stream extends FileInputStream to implement a
|
||||
* SocketInputStream. Note that this class should <b>NOT</b> be
|
||||
* public.
|
||||
*
|
||||
* @author Jonathan Payne
|
||||
* @author Arthur van Hoff
|
||||
*/
|
||||
class SocketInputStream extends FileInputStream {
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
private boolean eof;
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
private byte temp[];
|
||||
|
||||
/**
|
||||
* Creates a new SocketInputStream. Can only be called
|
||||
* by a Socket. This method needs to hang on to the owner Socket so
|
||||
* that the fd will not be closed.
|
||||
* @param impl the implemented socket input stream
|
||||
*/
|
||||
SocketInputStream(AbstractPlainSocketImpl impl) throws IOException {
|
||||
super(impl.getFileDescriptor());
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique {@link java.nio.channels.FileChannel FileChannel}
|
||||
* object associated with this file input stream.</p>
|
||||
*
|
||||
* The {@code getChannel} method of {@code SocketInputStream}
|
||||
* returns {@code null} since it is a socket based stream.</p>
|
||||
*
|
||||
* @return the file channel associated with this file input stream
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
public final FileChannel getChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads into an array of bytes at the specified offset using
|
||||
* the received socket primitive.
|
||||
* @param fd the FileDescriptor
|
||||
* @param b the buffer into which the data is read
|
||||
* @param off the start offset of the data
|
||||
* @param len the maximum number of bytes read
|
||||
* @param timeout the read timeout in ms
|
||||
* @return the actual number of bytes read, -1 is
|
||||
* returned when the end of the stream is reached.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private native int socketRead0(FileDescriptor fd,
|
||||
byte b[], int off, int len,
|
||||
int timeout)
|
||||
throws IOException;
|
||||
|
||||
// wrap native call to allow instrumentation
|
||||
/**
|
||||
* Reads into an array of bytes at the specified offset using
|
||||
* the received socket primitive.
|
||||
* @param fd the FileDescriptor
|
||||
* @param b the buffer into which the data is read
|
||||
* @param off the start offset of the data
|
||||
* @param len the maximum number of bytes read
|
||||
* @param timeout the read timeout in ms
|
||||
* @return the actual number of bytes read, -1 is
|
||||
* returned when the end of the stream is reached.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private int socketRead(FileDescriptor fd,
|
||||
byte b[], int off, int len,
|
||||
int timeout)
|
||||
throws IOException {
|
||||
return socketRead0(fd, b, off, len, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads into a byte array data from the socket.
|
||||
* @param b the buffer into which the data is read
|
||||
* @return the actual number of bytes read, -1 is
|
||||
* returned when the end of the stream is reached.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
public int read(byte b[]) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads into a byte array <i>b</i> at offset <i>off</i>,
|
||||
* <i>length</i> bytes of data.
|
||||
* @param b the buffer into which the data is read
|
||||
* @param off the start offset of the data
|
||||
* @param length the maximum number of bytes read
|
||||
* @return the actual number of bytes read, -1 is
|
||||
* returned when the end of the stream is reached.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
public int read(byte b[], int off, int length) throws IOException {
|
||||
return read(b, off, length, impl.getTimeout());
|
||||
}
|
||||
|
||||
int read(byte b[], int off, int length, int timeout) throws IOException {
|
||||
int n;
|
||||
|
||||
// EOF already encountered
|
||||
if (eof) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// connection reset
|
||||
if (impl.isConnectionReset()) {
|
||||
throw new SocketException("Connection reset");
|
||||
}
|
||||
|
||||
// bounds check
|
||||
if (length <= 0 || off < 0 || length > b.length - off) {
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException("length == " + length
|
||||
+ " off == " + off + " buffer length == " + b.length);
|
||||
}
|
||||
|
||||
// acquire file descriptor and do the read
|
||||
FileDescriptor fd = impl.acquireFD();
|
||||
try {
|
||||
n = socketRead(fd, b, off, length, timeout);
|
||||
if (n > 0) {
|
||||
return n;
|
||||
}
|
||||
} catch (ConnectionResetException rstExc) {
|
||||
impl.setConnectionReset();
|
||||
} finally {
|
||||
impl.releaseFD();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get here we are at EOF, the socket has been closed,
|
||||
* or the connection has been reset.
|
||||
*/
|
||||
if (impl.isClosedOrPending()) {
|
||||
throw new SocketException("Socket closed");
|
||||
}
|
||||
if (impl.isConnectionReset()) {
|
||||
throw new SocketException("Connection reset");
|
||||
}
|
||||
eof = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single byte from the socket.
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
if (eof) {
|
||||
return -1;
|
||||
}
|
||||
temp = new byte[1];
|
||||
int n = read(temp, 0, 1);
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return temp[0] & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips n bytes of input.
|
||||
* @param numbytes the number of bytes to skip
|
||||
* @return the actual number of bytes skipped.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
public long skip(long numbytes) throws IOException {
|
||||
if (numbytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
long n = numbytes;
|
||||
int buflen = (int) Math.min(1024, n);
|
||||
byte data[] = new byte[buflen];
|
||||
while (n > 0) {
|
||||
int r = read(data, 0, (int) Math.min((long) buflen, n));
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
n -= r;
|
||||
}
|
||||
return numbytes - n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes that can be read without blocking.
|
||||
* @return the number of immediately available bytes
|
||||
*/
|
||||
public int available() throws IOException {
|
||||
int available = impl.available();
|
||||
return eof ? 0 : available;
|
||||
}
|
||||
|
||||
void setEOF(boolean eof) {
|
||||
this.eof = eof;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// No longer used. Socket.getInputStream returns an
|
||||
// InputStream which calls Socket.close directly
|
||||
assert false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform class load-time initializations.
|
||||
*/
|
||||
private static native void init();
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, 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 java.net;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* This stream extends FileOutputStream to implement a
|
||||
* SocketOutputStream. Note that this class should <b>NOT</b> be
|
||||
* public.
|
||||
*
|
||||
* @author Jonathan Payne
|
||||
* @author Arthur van Hoff
|
||||
*/
|
||||
class SocketOutputStream extends FileOutputStream {
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
private byte temp[] = new byte[1];
|
||||
|
||||
/**
|
||||
* Creates a new SocketOutputStream. Can only be called
|
||||
* by a Socket. This method needs to hang on to the owner Socket so
|
||||
* that the fd will not be closed.
|
||||
* @param impl the socket output stream implemented
|
||||
*/
|
||||
SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
|
||||
super(impl.getFileDescriptor());
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique {@link java.nio.channels.FileChannel FileChannel}
|
||||
* object associated with this file output stream. </p>
|
||||
*
|
||||
* The {@code getChannel} method of {@code SocketOutputStream}
|
||||
* returns {@code null} since it is a socket based stream.</p>
|
||||
*
|
||||
* @return the file channel associated with this file output stream
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
public final FileChannel getChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to the socket.
|
||||
* @param fd the FileDescriptor
|
||||
* @param b the data to be written
|
||||
* @param off the start offset in the data
|
||||
* @param len the number of bytes that are written
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
|
||||
int len) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes to the socket with appropriate locking of the
|
||||
* FileDescriptor.
|
||||
* @param b the data to be written
|
||||
* @param off the start offset in the data
|
||||
* @param len the number of bytes that are written
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private void socketWrite(byte b[], int off, int len) throws IOException {
|
||||
|
||||
|
||||
if (len <= 0 || off < 0 || len > b.length - off) {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException("len == " + len
|
||||
+ " off == " + off + " buffer length == " + b.length);
|
||||
}
|
||||
|
||||
FileDescriptor fd = impl.acquireFD();
|
||||
try {
|
||||
socketWrite0(fd, b, off, len);
|
||||
} catch (SocketException se) {
|
||||
if (impl.isClosedOrPending()) {
|
||||
throw new SocketException("Socket closed");
|
||||
} else {
|
||||
throw se;
|
||||
}
|
||||
} finally {
|
||||
impl.releaseFD();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a byte to the socket.
|
||||
* @param b the data to be written
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
public void write(int b) throws IOException {
|
||||
temp[0] = (byte)b;
|
||||
socketWrite(temp, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of the buffer <i>b</i> to the socket.
|
||||
* @param b the data to be written
|
||||
* @throws SocketException If an I/O error has occurred.
|
||||
*/
|
||||
public void write(byte b[]) throws IOException {
|
||||
socketWrite(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <i>length</i> bytes from buffer <i>b</i> starting at
|
||||
* offset <i>len</i>.
|
||||
* @param b the data to be written
|
||||
* @param off the start offset in the data
|
||||
* @param len the number of bytes that are written
|
||||
* @throws SocketException If an I/O error has occurred.
|
||||
*/
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
socketWrite(b, off, len);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// No longer used. Socket.getOutputStream returns an
|
||||
// OutputStream which calls Socket.close directly
|
||||
assert false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform class load-time initializations.
|
||||
*/
|
||||
private static native void init();
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue