| /* |
| * 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(); |
| } |