| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.net; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.channels.SocketChannel; |
| import java.security.AccessController; |
| // BEGIN android-added |
| import java.util.logging.Logger; |
| import java.util.logging.Level; |
| // END android-added |
| |
| import org.apache.harmony.luni.net.NetUtil; |
| import org.apache.harmony.luni.net.PlainSocketImpl; |
| import org.apache.harmony.luni.platform.Platform; |
| import org.apache.harmony.luni.util.Msg; |
| import org.apache.harmony.luni.util.PriviAction; |
| |
| /** |
| * Provides a client-side TCP socket. |
| */ |
| public class Socket { |
| |
| SocketImpl impl; |
| |
| static SocketImplFactory factory; |
| |
| private volatile boolean isCreated = false; |
| |
| private boolean isBound = false; |
| |
| private boolean isConnected = false; |
| |
| private boolean isClosed = false; |
| |
| private boolean isInputShutdown = false; |
| |
| private boolean isOutputShutdown = false; |
| |
| private static class ConnectLock { |
| } |
| |
| private Object connectLock = new ConnectLock(); |
| |
| private Proxy proxy; |
| |
| static final int TCP_NODELAY = 4; |
| |
| static private Logger logger; |
| |
| static private Logger getLogger() { |
| if (logger == null) { |
| logger = Logger.getLogger(Socket.class.getName()); |
| } |
| return logger; |
| } |
| |
| // BEGIN android-removed: we do this statically, when we start the VM. |
| // static { |
| // Platform.getNetworkSystem().oneTimeInitialization(true); |
| // } |
| // END android-removed |
| |
| /** |
| * Creates a new unconnected socket. When a SocketImplFactory is defined it |
| * creates the internal socket implementation, otherwise the default socket |
| * implementation will be used for this socket. |
| * |
| * @see SocketImplFactory |
| * @see SocketImpl |
| */ |
| public Socket() { |
| impl = factory != null ? factory.createSocketImpl() |
| : new PlainSocketImpl(); |
| } |
| |
| /** |
| * Creates a new unconnected socket using the given proxy type. When a |
| * {@code SocketImplFactory} is defined it creates the internal socket |
| * implementation, otherwise the default socket implementation will be used |
| * for this socket. |
| * <p> |
| * Example that will create a socket connection through a {@code SOCKS} |
| * proxy server: <br> |
| * {@code Socket sock = new Socket(new Proxy(Proxy.Type.SOCKS, new |
| * InetSocketAddress("test.domain.org", 2130)));} |
| * |
| * @param proxy |
| * the specified proxy for this socket. |
| * @throws IllegalArgumentException |
| * if the argument {@code proxy} is {@code null} or of an |
| * invalid type. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given proxy. |
| * @see SocketImplFactory |
| * @see SocketImpl |
| */ |
| public Socket(Proxy proxy) { |
| if (null == proxy || Proxy.Type.HTTP == proxy.type()) { |
| // KA023=Proxy is null or invalid type |
| throw new IllegalArgumentException(Msg.getString("KA023")); //$NON-NLS-1$ |
| } |
| InetSocketAddress address = (InetSocketAddress) proxy.address(); |
| if (null != address) { |
| InetAddress addr = address.getAddress(); |
| String host; |
| if (null != addr) { |
| host = addr.getHostAddress(); |
| } else { |
| host = address.getHostName(); |
| } |
| int port = address.getPort(); |
| checkConnectPermission(host, port); |
| } |
| impl = factory != null ? factory.createSocketImpl() |
| : new PlainSocketImpl(proxy); |
| this.proxy = proxy; |
| } |
| |
| // BEGIN android-added |
| /** |
| * Tries to connect a socket to all IP addresses of the given hostname. |
| * |
| * @param dstName |
| * the target host name or IP address to connect to. |
| * @param dstPort |
| * the port on the target host to connect to. |
| * @param localAddress |
| * the address on the local host to bind to. |
| * @param localPort |
| * the port on the local host to bind to. |
| * @param streaming |
| * if {@code true} a streaming socket is returned, a datagram |
| * socket otherwise. |
| * @throws UnknownHostException |
| * if the host name could not be resolved into an IP address. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| */ |
| private void tryAllAddresses(String dstName, int dstPort, InetAddress |
| localAddress, int localPort, boolean streaming) throws IOException { |
| InetAddress[] dstAddresses = InetAddress.getAllByName(dstName); |
| // Loop through all the destination addresses except the last, trying to |
| // connect to each one and ignoring errors. There must be at least one |
| // address, or getAllByName would have thrown UnknownHostException. |
| InetAddress dstAddress; |
| for (int i = 0; i < dstAddresses.length - 1; i++) { |
| dstAddress = dstAddresses[i]; |
| try { |
| checkDestination(dstAddress, dstPort); |
| startupSocket(dstAddress, dstPort, localAddress, localPort, |
| streaming); |
| return; |
| } catch(SecurityException e1) { |
| getLogger().log(Level.INFO, dstAddress + "(" + dstPort + "): " + |
| e1.getClass().getName() + ": " + e1.getMessage()); |
| } catch(IOException e2) { |
| getLogger().log(Level.INFO, dstAddress + "(" + dstPort + "): " + |
| e2.getClass().getName() + ": " + e2.getMessage()); |
| } |
| } |
| |
| // Now try to connect to the last address in the array, handing back to |
| // the caller any exceptions that are thrown. |
| dstAddress = dstAddresses[dstAddresses.length - 1]; |
| checkDestination(dstAddress, dstPort); |
| startupSocket(dstAddress, dstPort, localAddress, localPort, streaming); |
| } |
| // END android-added |
| |
| /** |
| * Creates a new streaming socket connected to the target host specified by |
| * the parameters {@code dstName} and {@code dstPort}. The socket is bound |
| * to any available port on the local host. |
| * <p><strong>Implementation note:</strong> this implementation tries each |
| * IP address for the given hostname until it either connects successfully |
| * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the |
| * order specified by the system property {@code "java.net.preferIPv6Addresses"}. |
| * |
| * @param dstName |
| * the target host name or IP address to connect to. |
| * @param dstPort |
| * the port on the target host to connect to. |
| * @throws UnknownHostException |
| * if the host name could not be resolved into an IP address. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| */ |
| public Socket(String dstName, int dstPort) throws UnknownHostException, |
| IOException { |
| // BEGIN android-changed |
| this(dstName, dstPort, null, 0); |
| // END android-changed |
| } |
| |
| /** |
| * Creates a new streaming socket connected to the target host specified by |
| * the parameters {@code dstName} and {@code dstPort}. On the local endpoint |
| * the socket is bound to the given address {@code localAddress} on port |
| * {@code localPort}. |
| * |
| * If {@code host} is {@code null} a loopback address is used to connect to. |
| * <p><strong>Implementation note:</strong> this implementation tries each |
| * IP address for the given hostname until it either connects successfully |
| * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the |
| * order specified by the system property {@code "java.net.preferIPv6Addresses"}. |
| * |
| * @param dstName |
| * the target host name or IP address to connect to. |
| * @param dstPort |
| * the port on the target host to connect to. |
| * @param localAddress |
| * the address on the local host to bind to. |
| * @param localPort |
| * the port on the local host to bind to. |
| * @throws UnknownHostException |
| * if the host name could not be resolved into an IP address. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| */ |
| public Socket(String dstName, int dstPort, InetAddress localAddress, |
| int localPort) throws IOException { |
| this(); |
| // BEGIN android-changed |
| tryAllAddresses(dstName, dstPort, localAddress, localPort, true); |
| // END android-changed |
| } |
| |
| /** |
| * Creates a new streaming or datagram socket connected to the target host |
| * specified by the parameters {@code hostName} and {@code port}. The socket |
| * is bound to any available port on the local host. |
| * <p><strong>Implementation note:</strong> this implementation tries each |
| * IP address for the given hostname until it either connects successfully |
| * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the |
| * order specified by the system property {@code "java.net.preferIPv6Addresses"}. |
| * |
| * @param hostName |
| * the target host name or IP address to connect to. |
| * @param port |
| * the port on the target host to connect to. |
| * @param streaming |
| * if {@code true} a streaming socket is returned, a datagram |
| * socket otherwise. |
| * @throws UnknownHostException |
| * if the host name could not be resolved into an IP address. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| * @deprecated Use {@code Socket(String, int)} instead of this for streaming |
| * sockets or an appropriate constructor of {@code |
| * DatagramSocket} for UDP transport. |
| */ |
| @Deprecated |
| public Socket(String hostName, int port, boolean streaming) |
| throws IOException { |
| this(); |
| // BEGIN android-changed |
| tryAllAddresses(hostName, port, null, 0, streaming); |
| // END android-changed |
| } |
| |
| /** |
| * Creates a new streaming socket connected to the target host specified by |
| * the parameters {@code dstAddress} and {@code dstPort}. The socket is |
| * bound to any available port on the local host. |
| * |
| * @param dstAddress |
| * the target host address to connect to. |
| * @param dstPort |
| * the port on the target host to connect to. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| */ |
| public Socket(InetAddress dstAddress, int dstPort) throws IOException { |
| this(); |
| checkDestination(dstAddress, dstPort); |
| startupSocket(dstAddress, dstPort, null, 0, true); |
| } |
| |
| /** |
| * Creates a new streaming socket connected to the target host specified by |
| * the parameters {@code dstAddress} and {@code dstPort}. On the local |
| * endpoint the socket is bound to the given address {@code localAddress} on |
| * port {@code localPort}. |
| * |
| * @param dstAddress |
| * the target host address to connect to. |
| * @param dstPort |
| * the port on the target host to connect to. |
| * @param localAddress |
| * the address on the local host to bind to. |
| * @param localPort |
| * the port on the local host to bind to. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| */ |
| public Socket(InetAddress dstAddress, int dstPort, |
| InetAddress localAddress, int localPort) throws IOException { |
| this(); |
| checkDestination(dstAddress, dstPort); |
| startupSocket(dstAddress, dstPort, localAddress, localPort, true); |
| } |
| |
| /** |
| * Creates a new streaming or datagram socket connected to the target host |
| * specified by the parameters {@code addr} and {@code port}. The socket is |
| * bound to any available port on the local host. |
| * |
| * @param addr |
| * the Internet address to connect to. |
| * @param port |
| * the port on the target host to connect to. |
| * @param streaming |
| * if {@code true} a streaming socket is returned, a datagram |
| * socket otherwise. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| * @throws SecurityException |
| * if a security manager exists and it denies the permission to |
| * connect to the given address and port. |
| * @deprecated Use {@code Socket(InetAddress, int)} instead of this for |
| * streaming sockets or an appropriate constructor of {@code |
| * DatagramSocket} for UDP transport. |
| */ |
| @Deprecated |
| public Socket(InetAddress addr, int port, boolean streaming) |
| throws IOException { |
| this(); |
| checkDestination(addr, port); |
| startupSocket(addr, port, null, 0, streaming); |
| } |
| |
| /** |
| * Creates an unconnected socket with the given socket implementation. |
| * |
| * @param anImpl |
| * the socket implementation to be used. |
| * @throws SocketException |
| * if an error occurs while creating the socket. |
| */ |
| protected Socket(SocketImpl anImpl) throws SocketException { |
| impl = anImpl; |
| } |
| |
| /** |
| * Checks whether the connection destination satisfies the security policy |
| * and the validity of the port range. |
| * |
| * @param destAddr |
| * the destination host address. |
| * @param dstPort |
| * the port on the destination host. |
| */ |
| void checkDestination(InetAddress destAddr, int dstPort) { |
| if (dstPort < 0 || dstPort > 65535) { |
| throw new IllegalArgumentException(Msg.getString("K0032")); //$NON-NLS-1$ |
| } |
| // BEGIN android-changed |
| checkConnectPermission(destAddr.getHostAddress(), dstPort); |
| // END android-changed |
| } |
| |
| /** |
| * Checks whether the connection destination satisfies the security policy. |
| * |
| * @param hostname |
| * the destination hostname. |
| * @param dstPort |
| * the port on the destination host. |
| */ |
| private void checkConnectPermission(String hostname, int dstPort) { |
| SecurityManager security = System.getSecurityManager(); |
| if (security != null) { |
| security.checkConnect(hostname, dstPort); |
| } |
| } |
| |
| /** |
| * Closes the socket. It is not possible to reconnect or rebind to this |
| * socket thereafter which means a new socket instance has to be created. |
| * |
| * @throws IOException |
| * if an error occurs while closing the socket. |
| */ |
| public synchronized void close() throws IOException { |
| isClosed = true; |
| impl.close(); |
| } |
| |
| /** |
| * Gets the IP address of the target host this socket is connected to. |
| * |
| * @return the IP address of the connected target host or {@code null} if |
| * this socket is not yet connected. |
| */ |
| public InetAddress getInetAddress() { |
| if (!isConnected()) { |
| return null; |
| } |
| return impl.getInetAddress(); |
| } |
| |
| /** |
| * Gets an input stream to read data from this socket. |
| * |
| * @return the byte-oriented input stream. |
| * @throws IOException |
| * if an error occurs while creating the input stream or the |
| * socket is in an invalid state. |
| */ |
| public InputStream getInputStream() throws IOException { |
| checkClosedAndCreate(false); |
| if (isInputShutdown()) { |
| throw new SocketException(Msg.getString("K0321")); //$NON-NLS-1$ |
| } |
| return impl.getInputStream(); |
| } |
| |
| /** |
| * Gets the setting of the socket option {@code SocketOptions.SO_KEEPALIVE}. |
| * |
| * @return {@code true} if the {@code SocketOptions.SO_KEEPALIVE} is |
| * enabled, {@code false} otherwise. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_KEEPALIVE |
| */ |
| public boolean getKeepAlive() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Boolean) impl.getOption(SocketOptions.SO_KEEPALIVE)) |
| .booleanValue(); |
| } |
| |
| /** |
| * Gets the local IP address this socket is bound to. |
| * |
| * @return the local IP address of this socket or {@code InetAddress.ANY} if |
| * the socket is unbound. |
| */ |
| public InetAddress getLocalAddress() { |
| if (!isBound()) { |
| return Inet4Address.ANY; |
| } |
| return Platform.getNetworkSystem().getSocketLocalAddress(impl.fd); |
| } |
| |
| /** |
| * Gets the local port this socket is bound to. |
| * |
| * @return the local port of this socket or {@code -1} if the socket is |
| * unbound. |
| */ |
| public int getLocalPort() { |
| if (!isBound()) { |
| return -1; |
| } |
| return impl.getLocalPort(); |
| } |
| |
| /** |
| * Gets an output stream to write data into this socket. |
| * |
| * @return the byte-oriented output stream. |
| * @throws IOException |
| * if an error occurs while creating the output stream or the |
| * socket is in an invalid state. |
| */ |
| public OutputStream getOutputStream() throws IOException { |
| checkClosedAndCreate(false); |
| if (isOutputShutdown()) { |
| throw new SocketException(Msg.getString("KA00f")); //$NON-NLS-1$ |
| } |
| return impl.getOutputStream(); |
| } |
| |
| /** |
| * Gets the port number of the target host this socket is connected to. |
| * |
| * @return the port number of the connected target host or {@code 0} if this |
| * socket is not yet connected. |
| */ |
| public int getPort() { |
| if (!isConnected()) { |
| return 0; |
| } |
| return impl.getPort(); |
| } |
| |
| /** |
| * Gets the value of the socket option {@code SocketOptions.SO_LINGER}. |
| * |
| * @return the current value of the option {@code SocketOptions.SO_LINGER} |
| * or {@code -1} if this option is disabled. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_LINGER |
| */ |
| public int getSoLinger() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Integer) impl.getOption(SocketOptions.SO_LINGER)).intValue(); |
| } |
| |
| /** |
| * Gets the receive buffer size of this socket. |
| * |
| * @return the current value of the option {@code SocketOptions.SO_RCVBUF}. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_RCVBUF |
| */ |
| public synchronized int getReceiveBufferSize() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue(); |
| } |
| |
| /** |
| * Gets the send buffer size of this socket. |
| * |
| * @return the current value of the option {@code SocketOptions.SO_SNDBUF}. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_SNDBUF |
| */ |
| public synchronized int getSendBufferSize() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue(); |
| } |
| |
| /** |
| * Gets the timeout for this socket during which a reading operation shall |
| * block while waiting for data. |
| * |
| * @return the current value of the option {@code SocketOptions.SO_TIMEOUT} |
| * or {@code 0} which represents an infinite timeout. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_TIMEOUT |
| */ |
| public synchronized int getSoTimeout() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue(); |
| } |
| |
| /** |
| * Gets the setting of the socket option {@code SocketOptions.TCP_NODELAY}. |
| * |
| * @return {@code true} if the {@code SocketOptions.TCP_NODELAY} is enabled, |
| * {@code false} otherwise. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#TCP_NODELAY |
| */ |
| public boolean getTcpNoDelay() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Boolean) impl.getOption(SocketOptions.TCP_NODELAY)) |
| .booleanValue(); |
| } |
| |
| /** |
| * Sets the state of the {@code SocketOptions.SO_KEEPALIVE} for this socket. |
| * |
| * @param value |
| * the state whether this option is enabled or not. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#SO_KEEPALIVE |
| */ |
| public void setKeepAlive(boolean value) throws SocketException { |
| if (impl != null) { |
| checkClosedAndCreate(true); |
| impl.setOption(SocketOptions.SO_KEEPALIVE, value ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| } |
| |
| /** |
| * Sets the internal factory for creating socket implementations. This may |
| * only be executed once during the lifetime of the application. |
| * |
| * @param fac |
| * the socket implementation factory to be set. |
| * @throws IOException |
| * if the factory has been already set. |
| */ |
| public static synchronized void setSocketImplFactory(SocketImplFactory fac) |
| throws IOException { |
| SecurityManager security = System.getSecurityManager(); |
| if (security != null) { |
| security.checkSetFactory(); |
| } |
| if (factory != null) { |
| throw new SocketException(Msg.getString("K0044")); //$NON-NLS-1$ |
| } |
| factory = fac; |
| } |
| |
| /** |
| * Sets the send buffer size of this socket. |
| * |
| * @param size |
| * the buffer size in bytes. This value must be a positive number |
| * greater than {@code 0}. |
| * @throws SocketException |
| * if an error occurs while setting the size or the given value |
| * is an invalid size. |
| * @see SocketOptions#SO_SNDBUF |
| */ |
| public synchronized void setSendBufferSize(int size) throws SocketException { |
| checkClosedAndCreate(true); |
| if (size < 1) { |
| throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ |
| } |
| impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); |
| } |
| |
| /** |
| * Sets the receive buffer size of this socket. |
| * |
| * @param size |
| * the buffer size in bytes. This value must be a positive number |
| * greater than {@code 0}. |
| * @throws SocketException |
| * if an error occurs while setting the size or the given value |
| * is an invalid size. |
| * @see SocketOptions#SO_RCVBUF |
| */ |
| public synchronized void setReceiveBufferSize(int size) |
| throws SocketException { |
| checkClosedAndCreate(true); |
| if (size < 1) { |
| throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ |
| } |
| impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); |
| } |
| |
| /** |
| * Sets the state of the {@code SocketOptions.SO_LINGER} with the given |
| * timeout in seconds. The timeout value for this option is silently limited |
| * to the maximum of {@code 65535}. |
| * |
| * @param on |
| * the state whether this option is enabled or not. |
| * @param timeout |
| * the linger timeout value in seconds. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#SO_LINGER |
| */ |
| public void setSoLinger(boolean on, int timeout) throws SocketException { |
| checkClosedAndCreate(true); |
| if (on && timeout < 0) { |
| throw new IllegalArgumentException(Msg.getString("K0045")); //$NON-NLS-1$ |
| } |
| // BEGIN android-changed |
| /* |
| * The spec indicates that the right way to turn off an option |
| * is to pass Boolean.FALSE, so that's what we do here. |
| */ |
| if (on) { |
| if (timeout > 65535) { |
| timeout = 65535; |
| } |
| impl.setOption(SocketOptions.SO_LINGER, Integer.valueOf(timeout)); |
| } else { |
| impl.setOption(SocketOptions.SO_LINGER, Boolean.FALSE); |
| } |
| // END android-changed |
| } |
| |
| /** |
| * Sets the reading timeout in milliseconds for this socket. The read |
| * operation will block indefinitely if this option value is set to {@code |
| * 0}. The timeout must be set before calling the read operation. A |
| * {@code SocketTimeoutException} is thrown when this timeout expires. |
| * |
| * @param timeout |
| * the reading timeout value as number greater than {@code 0} or |
| * {@code 0} for an infinite timeout. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#SO_TIMEOUT |
| */ |
| public synchronized void setSoTimeout(int timeout) throws SocketException { |
| checkClosedAndCreate(true); |
| if (timeout < 0) { |
| throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$ |
| } |
| impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); |
| } |
| |
| /** |
| * Sets the state of the {@code SocketOptions.TCP_NODELAY} for this socket. |
| * |
| * @param on |
| * the state whether this option is enabled or not. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#TCP_NODELAY |
| */ |
| public void setTcpNoDelay(boolean on) throws SocketException { |
| checkClosedAndCreate(true); |
| impl.setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); |
| } |
| |
| /** |
| * Creates a stream socket, binds it to the nominated local address/port, |
| * then connects it to the nominated destination address/port. |
| * |
| * @param dstAddress |
| * the destination host address. |
| * @param dstPort |
| * the port on the destination host. |
| * @param localAddress |
| * the address on the local machine to bind. |
| * @param localPort |
| * the port on the local machine to bind. |
| * @throws IOException |
| * thrown if an error occurs during the bind or connect |
| * operations. |
| */ |
| void startupSocket(InetAddress dstAddress, int dstPort, |
| InetAddress localAddress, int localPort, boolean streaming) |
| throws IOException { |
| |
| if (localPort < 0 || localPort > 65535) { |
| throw new IllegalArgumentException(Msg.getString("K0046")); //$NON-NLS-1$ |
| } |
| |
| InetAddress addr = localAddress == null ? Inet4Address.ANY |
| : localAddress; |
| synchronized (this) { |
| impl.create(streaming); |
| isCreated = true; |
| try { |
| if (!streaming || !NetUtil.usingSocks(proxy)) { |
| impl.bind(addr, localPort); |
| } |
| isBound = true; |
| impl.connect(dstAddress, dstPort); |
| isConnected = true; |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Returns a {@code String} containing a concise, human-readable description of the |
| * socket. |
| * |
| * @return the textual representation of this socket. |
| */ |
| @Override |
| public String toString() { |
| if (!isConnected()) { |
| return "Socket[unconnected]"; //$NON-NLS-1$ |
| } |
| return impl.toString(); |
| } |
| |
| /** |
| * Closes the input stream of this socket. Any further data sent to this |
| * socket will be discarded. Reading from this socket after this method has |
| * been called will return the value {@code EOF}. |
| * |
| * @throws IOException |
| * if an error occurs while closing the socket input stream. |
| * @throws SocketException |
| * if the input stream is already closed. |
| */ |
| public void shutdownInput() throws IOException { |
| if (isInputShutdown()) { |
| throw new SocketException(Msg.getString("K0321")); //$NON-NLS-1$ |
| } |
| checkClosedAndCreate(false); |
| impl.shutdownInput(); |
| isInputShutdown = true; |
| } |
| |
| /** |
| * Closes the output stream of this socket. All buffered data will be sent |
| * followed by the termination sequence. Writing to the closed output stream |
| * will cause an {@code IOException}. |
| * |
| * @throws IOException |
| * if an error occurs while closing the socket output stream. |
| * @throws SocketException |
| * if the output stream is already closed. |
| */ |
| public void shutdownOutput() throws IOException { |
| if (isOutputShutdown()) { |
| throw new SocketException(Msg.getString("KA00f")); //$NON-NLS-1$ |
| } |
| checkClosedAndCreate(false); |
| impl.shutdownOutput(); |
| isOutputShutdown = true; |
| } |
| |
| /** |
| * Checks whether the socket is closed, and throws an exception. Otherwise |
| * creates the underlying SocketImpl. |
| * |
| * @throws SocketException |
| * if the socket is closed. |
| */ |
| private void checkClosedAndCreate(boolean create) throws SocketException { |
| if (isClosed()) { |
| throw new SocketException(Msg.getString("K003d")); //$NON-NLS-1$ |
| } |
| if (!create) { |
| if (!isConnected()) { |
| throw new SocketException(Msg.getString("K0320")); //$NON-NLS-1$ |
| // a connected socket must be created |
| } |
| |
| /* |
| * return directly to fix a possible bug, if !create, should return |
| * here |
| */ |
| return; |
| } |
| if (isCreated) { |
| return; |
| } |
| synchronized (this) { |
| if (isCreated) { |
| return; |
| } |
| try { |
| impl.create(true); |
| } catch (SocketException e) { |
| throw e; |
| } catch (IOException e) { |
| throw new SocketException(e.toString()); |
| } |
| isCreated = true; |
| } |
| } |
| |
| /** |
| * Gets the local address and port of this socket as a SocketAddress or |
| * {@code null} if the socket is unbound. This is useful on multihomed |
| * hosts. |
| * |
| * @return the bound local socket address and port. |
| */ |
| public SocketAddress getLocalSocketAddress() { |
| if (!isBound()) { |
| return null; |
| } |
| return new InetSocketAddress(getLocalAddress(), getLocalPort()); |
| } |
| |
| /** |
| * Gets the remote address and port of this socket as a {@code |
| * SocketAddress} or {@code null} if the socket is not connected. |
| * |
| * @return the remote socket address and port. |
| */ |
| public SocketAddress getRemoteSocketAddress() { |
| if (!isConnected()) { |
| return null; |
| } |
| return new InetSocketAddress(getInetAddress(), getPort()); |
| } |
| |
| /** |
| * Returns whether this socket is bound to a local address and port. |
| * |
| * @return {@code true} if the socket is bound to a local address, {@code |
| * false} otherwise. |
| */ |
| public boolean isBound() { |
| return isBound; |
| } |
| |
| /** |
| * Returns whether this socket is connected to a remote host. |
| * |
| * @return {@code true} if the socket is connected, {@code false} otherwise. |
| */ |
| public boolean isConnected() { |
| return isConnected; |
| } |
| |
| /** |
| * Returns whether this socket is closed. |
| * |
| * @return {@code true} if the socket is closed, {@code false} otherwise. |
| */ |
| public boolean isClosed() { |
| return isClosed; |
| } |
| |
| /** |
| * Binds this socket to the given local host address and port specified by |
| * the SocketAddress {@code localAddr}. If {@code localAddr} is set to |
| * {@code null}, this socket will be bound to an available local address on |
| * any free port. |
| * |
| * @param localAddr |
| * the specific address and port on the local machine to bind to. |
| * @throws IllegalArgumentException |
| * if the given SocketAddress is invalid or not supported. |
| * @throws IOException |
| * if the socket is already bound or an error occurs while |
| * binding. |
| */ |
| public void bind(SocketAddress localAddr) throws IOException { |
| checkClosedAndCreate(true); |
| if (isBound()) { |
| throw new BindException(Msg.getString("K0315")); //$NON-NLS-1$ |
| } |
| |
| int port = 0; |
| InetAddress addr = Inet4Address.ANY; |
| if (localAddr != null) { |
| if (!(localAddr instanceof InetSocketAddress)) { |
| throw new IllegalArgumentException(Msg.getString( |
| "K0316", localAddr.getClass())); //$NON-NLS-1$ |
| } |
| InetSocketAddress inetAddr = (InetSocketAddress) localAddr; |
| if ((addr = inetAddr.getAddress()) == null) { |
| throw new SocketException(Msg.getString( |
| "K0317", inetAddr.getHostName())); //$NON-NLS-1$ |
| } |
| port = inetAddr.getPort(); |
| } |
| |
| synchronized (this) { |
| try { |
| impl.bind(addr, port); |
| isBound = true; |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Connects this socket to the given remote host address and port specified |
| * by the SocketAddress {@code remoteAddr}. |
| * |
| * @param remoteAddr |
| * the address and port of the remote host to connect to. |
| * @throws IllegalArgumentException |
| * if the given SocketAddress is invalid or not supported. |
| * @throws IOException |
| * if the socket is already connected or an error occurs while |
| * connecting. |
| */ |
| public void connect(SocketAddress remoteAddr) throws IOException { |
| connect(remoteAddr, 0); |
| } |
| |
| /** |
| * Connects this socket to the given remote host address and port specified |
| * by the SocketAddress {@code remoteAddr} with the specified timeout. The |
| * connecting method will block until the connection is established or an |
| * error occurred. |
| * |
| * @param remoteAddr |
| * the address and port of the remote host to connect to. |
| * @param timeout |
| * the timeout value in milliseconds or {@code 0} for an infinite |
| * timeout. |
| * @throws IllegalArgumentException |
| * if the given SocketAddress is invalid or not supported or the |
| * timeout value is negative. |
| * @throws IOException |
| * if the socket is already connected or an error occurs while |
| * connecting. |
| */ |
| public void connect(SocketAddress remoteAddr, int timeout) |
| throws IOException { |
| checkClosedAndCreate(true); |
| if (timeout < 0) { |
| throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$ |
| } |
| if (isConnected()) { |
| throw new SocketException(Msg.getString("K0079")); //$NON-NLS-1$ |
| } |
| if (remoteAddr == null) { |
| throw new IllegalArgumentException(Msg.getString("K0318")); //$NON-NLS-1$ |
| } |
| |
| if (!(remoteAddr instanceof InetSocketAddress)) { |
| throw new IllegalArgumentException(Msg.getString( |
| "K0316", remoteAddr.getClass())); //$NON-NLS-1$ |
| } |
| InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; |
| InetAddress addr; |
| if ((addr = inetAddr.getAddress()) == null) { |
| throw new UnknownHostException(Msg.getString("K0317", remoteAddr));//$NON-NLS-1$ |
| } |
| int port = inetAddr.getPort(); |
| |
| checkDestination(addr, port); |
| synchronized (connectLock) { |
| try { |
| if (!isBound()) { |
| // socket allready created at this point by earlier call or |
| // checkClosedAndCreate this caused us to lose socket |
| // options on create |
| // impl.create(true); |
| if (!NetUtil.usingSocks(proxy)) { |
| impl.bind(Inet4Address.ANY, 0); |
| } |
| isBound = true; |
| } |
| impl.connect(remoteAddr, timeout); |
| isConnected = true; |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Returns whether the incoming channel of the socket has already been |
| * closed. |
| * |
| * @return {@code true} if reading from this socket is not possible anymore, |
| * {@code false} otherwise. |
| */ |
| public boolean isInputShutdown() { |
| return isInputShutdown; |
| } |
| |
| /** |
| * Returns whether the outgoing channel of the socket has already been |
| * closed. |
| * |
| * @return {@code true} if writing to this socket is not possible anymore, |
| * {@code false} otherwise. |
| */ |
| public boolean isOutputShutdown() { |
| return isOutputShutdown; |
| } |
| |
| /** |
| * Sets the state of the {@code SocketOptions.SO_REUSEADDR} for this socket. |
| * |
| * @param reuse |
| * the state whether this option is enabled or not. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#SO_REUSEADDR |
| */ |
| public void setReuseAddress(boolean reuse) throws SocketException { |
| checkClosedAndCreate(true); |
| impl.setOption(SocketOptions.SO_REUSEADDR, reuse ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| |
| /** |
| * Gets the setting of the socket option {@code SocketOptions.SO_REUSEADDR}. |
| * |
| * @return {@code true} if the {@code SocketOptions.SO_REUSEADDR} is |
| * enabled, {@code false} otherwise. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_REUSEADDR |
| */ |
| public boolean getReuseAddress() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)) |
| .booleanValue(); |
| } |
| |
| /** |
| * Sets the state of the {@code SocketOptions.SO_OOBINLINE} for this socket. |
| * When this option is enabled urgent data can be received in-line with |
| * normal data. |
| * |
| * @param oobinline |
| * whether this option is enabled or not. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#SO_OOBINLINE |
| */ |
| public void setOOBInline(boolean oobinline) throws SocketException { |
| checkClosedAndCreate(true); |
| impl.setOption(SocketOptions.SO_OOBINLINE, oobinline ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| |
| /** |
| * Gets the setting of the socket option {@code SocketOptions.SO_OOBINLINE}. |
| * |
| * @return {@code true} if the {@code SocketOptions.SO_OOBINLINE} is |
| * enabled, {@code false} otherwise. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#SO_OOBINLINE |
| */ |
| public boolean getOOBInline() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Boolean) impl.getOption(SocketOptions.SO_OOBINLINE)) |
| .booleanValue(); |
| } |
| |
| /** |
| * Sets the value of the {@code SocketOptions.IP_TOS} for this socket. See |
| * the specification RFC 1349 for more information about the type of service |
| * field. |
| * |
| * @param value |
| * the value to be set for this option with a valid range of |
| * {@code 0-255}. |
| * @throws SocketException |
| * if an error occurs while setting the option. |
| * @see SocketOptions#IP_TOS |
| */ |
| public void setTrafficClass(int value) throws SocketException { |
| checkClosedAndCreate(true); |
| if (value < 0 || value > 255) { |
| throw new IllegalArgumentException(); |
| } |
| impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); |
| } |
| |
| /** |
| * Gets the value of the socket option {@code SocketOptions.IP_TOS}. |
| * |
| * @return the value which represents the type of service. |
| * @throws SocketException |
| * if an error occurs while reading the socket option. |
| * @see SocketOptions#IP_TOS |
| */ |
| public int getTrafficClass() throws SocketException { |
| checkClosedAndCreate(true); |
| return ((Number) impl.getOption(SocketOptions.IP_TOS)).intValue(); |
| } |
| |
| /** |
| * Sends the given single byte data which is represented by the lowest octet |
| * of {@code value} as "TCP urgent data". |
| * |
| * @param value |
| * the byte of urgent data to be sent. |
| * @throws IOException |
| * if an error occurs while sending urgent data. |
| */ |
| public void sendUrgentData(int value) throws IOException { |
| if (!impl.supportsUrgentData()) { |
| throw new SocketException(Msg.getString("K0333")); //$NON-NLS-1$ |
| } |
| impl.sendUrgentData(value); |
| } |
| |
| /** |
| * Set the appropriate flags for a socket created by {@code |
| * ServerSocket.accept()}. |
| * |
| * @see ServerSocket#implAccept |
| */ |
| void accepted() { |
| isCreated = isBound = isConnected = true; |
| } |
| |
| static boolean preferIPv4Stack() { |
| String result = AccessController.doPrivileged(new PriviAction<String>( |
| "java.net.preferIPv4Stack")); //$NON-NLS-1$ |
| return "true".equals(result); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Gets the SocketChannel of this socket, if one is available. The current |
| * implementation of this method returns always {@code null}. |
| * |
| * @return the related SocketChannel or {@code null} if no channel exists. |
| */ |
| public SocketChannel getChannel() { |
| return null; |
| } |
| |
| /** |
| * Sets performance preferences for connectionTime, latency and bandwidth. |
| * <p> |
| * This method does currently nothing. |
| * |
| * @param connectionTime |
| * the value representing the importance of a short connecting |
| * time. |
| * @param latency |
| * the value representing the importance of low latency. |
| * @param bandwidth |
| * the value representing the importance of high bandwidth. |
| */ |
| public void setPerformancePreferences(int connectionTime, int latency, |
| int bandwidth) { |
| // Our socket implementation only provide one protocol: TCP/IP, so |
| // we do nothing for this method |
| } |
| } |