/* * 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 MULTICAST_IF = 1; static final int MULTICAST_TTL = 2; static final int TCP_NODELAY = 4; static final int FLAG_SHUTDOWN = 8; 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 } }