| /* |
| * 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.Closeable; |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.channels.SocketChannel; |
| import libcore.io.IoBridge; |
| |
| /** |
| * Provides a client-side TCP socket. |
| */ |
| public class Socket implements Closeable { |
| private static SocketImplFactory factory; |
| |
| final SocketImpl impl; |
| private final Proxy proxy; |
| |
| 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 InetAddress localAddress = Inet6Address.ANY; |
| |
| private final Object connectLock = new Object(); |
| |
| /** |
| * 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() { |
| this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl(); |
| this.proxy = null; |
| } |
| |
| /** |
| * 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. |
| * @see SocketImplFactory |
| * @see SocketImpl |
| */ |
| public Socket(Proxy proxy) { |
| if (proxy == null || proxy.type() == Proxy.Type.HTTP) { |
| throw new IllegalArgumentException("Invalid proxy: " + proxy); |
| } |
| this.proxy = proxy; |
| this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl(proxy); |
| } |
| |
| /** |
| * 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. |
| */ |
| 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 (IOException ex) { |
| } |
| } |
| |
| // 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); |
| } |
| |
| /** |
| * 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>This implementation tries each IP address for the given hostname (in |
| * <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order) |
| * until it either connects successfully or it exhausts the set. |
| * |
| * @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. |
| */ |
| public Socket(String dstName, int dstPort) throws UnknownHostException, IOException { |
| this(dstName, dstPort, null, 0); |
| } |
| |
| /** |
| * 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>This implementation tries each IP address for the given hostname (in |
| * <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order) |
| * until it either connects successfully or it exhausts the set. |
| * |
| * @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. |
| */ |
| public Socket(String dstName, int dstPort, InetAddress localAddress, int localPort) throws IOException { |
| this(); |
| tryAllAddresses(dstName, dstPort, localAddress, localPort, true); |
| } |
| |
| /** |
| * 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>This implementation tries each IP address for the given hostname (in |
| * <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order) |
| * until it either connects successfully or it exhausts the set. |
| * |
| * @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. |
| * @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(); |
| tryAllAddresses(hostName, port, null, 0, streaming); |
| } |
| |
| /** |
| * 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 using a wildcard address. |
| * |
| * @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. |
| */ |
| 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}. |
| * |
| * <p>On the local endpoint the socket is bound to the given address {@code localAddress} on |
| * port {@code localPort}. If {@code localAddress} is {@code null} the socket will be bound to a |
| * wildcard address. |
| * |
| * @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, or null. |
| * @param localPort |
| * the port on the local host to bind to. |
| * @throws IOException |
| * if an error occurs while creating the socket. |
| */ |
| 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 using a wildcard address. |
| * |
| * @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. |
| * @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 impl |
| * the socket implementation to be used. |
| * @throws SocketException |
| * if an error occurs while creating the socket. |
| */ |
| protected Socket(SocketImpl impl) throws SocketException { |
| this.impl = impl; |
| this.proxy = null; |
| } |
| |
| /** |
| * 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. |
| */ |
| private void checkDestination(InetAddress destAddr, int dstPort) { |
| if (dstPort < 0 || dstPort > 65535) { |
| throw new IllegalArgumentException("Port out of range: " + 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; |
| isConnected = false; |
| // RI compatibility: the RI returns the any address (but the original local port) after |
| // close. |
| localAddress = Inet6Address.ANY; |
| impl.close(); |
| } |
| |
| /** |
| * Sets the Socket and its related SocketImpl state as if a successful close() took place, |
| * without actually performing an OS close(). |
| * |
| * @hide used in java.nio |
| */ |
| public void onClose() { |
| isClosed = true; |
| isConnected = false; |
| // RI compatibility: the RI returns the any address (but the original local port) after |
| // close. |
| localAddress = Inet6Address.ANY; |
| impl.onClose(); |
| } |
| |
| /** |
| * Returns the IP address of the target host this socket is connected to, or null if this |
| * socket is not yet connected. |
| */ |
| public InetAddress getInetAddress() { |
| if (!isConnected()) { |
| return null; |
| } |
| return impl.getInetAddress(); |
| } |
| |
| /** |
| * Returns an input stream to read data from this socket. If the socket has an associated |
| * {@link SocketChannel} and that channel is in non-blocking mode then reads from the |
| * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}. |
| * |
| * @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 { |
| checkOpenAndCreate(false); |
| if (isInputShutdown()) { |
| throw new SocketException("Socket input is shutdown"); |
| } |
| return impl.getInputStream(); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_KEEPALIVE} setting. |
| */ |
| public boolean getKeepAlive() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Boolean) impl.getOption(SocketOptions.SO_KEEPALIVE); |
| } |
| |
| /** |
| * Returns the local IP address this socket is bound to, or an address for which |
| * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is closed or unbound. |
| */ |
| public InetAddress getLocalAddress() { |
| return localAddress; |
| } |
| |
| /** |
| * Returns the local port this socket is bound to, or -1 if the socket is unbound. If the socket |
| * has been closed this method will still return the local port the socket was bound to. |
| */ |
| public int getLocalPort() { |
| if (!isBound()) { |
| return -1; |
| } |
| return impl.getLocalPort(); |
| } |
| |
| /** |
| * Returns an output stream to write data into this socket. If the socket has an associated |
| * {@link SocketChannel} and that channel is in non-blocking mode then writes to the |
| * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}. |
| * |
| * @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 { |
| checkOpenAndCreate(false); |
| if (isOutputShutdown()) { |
| throw new SocketException("Socket output is shutdown"); |
| } |
| return impl.getOutputStream(); |
| } |
| |
| /** |
| * Returns the port number of the target host this socket is connected to, or 0 if this socket |
| * is not yet connected. |
| */ |
| public int getPort() { |
| if (!isConnected()) { |
| return 0; |
| } |
| return impl.getPort(); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_LINGER linger} timeout in seconds, or -1 |
| * for no linger (i.e. {@code close} will return immediately). |
| */ |
| public int getSoLinger() throws SocketException { |
| checkOpenAndCreate(true); |
| // The RI explicitly guarantees this idiocy in the SocketOptions.setOption documentation. |
| Object value = impl.getOption(SocketOptions.SO_LINGER); |
| if (value instanceof Integer) { |
| return (Integer) value; |
| } else { |
| return -1; |
| } |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}. |
| */ |
| public synchronized int getReceiveBufferSize() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Integer) impl.getOption(SocketOptions.SO_RCVBUF); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_SNDBUF send buffer size}. |
| */ |
| public synchronized int getSendBufferSize() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Integer) impl.getOption(SocketOptions.SO_SNDBUF); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_TIMEOUT receive timeout}. |
| */ |
| public synchronized int getSoTimeout() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Integer) impl.getOption(SocketOptions.SO_TIMEOUT); |
| } |
| |
| /** |
| * Returns this socket's {@code SocketOptions#TCP_NODELAY} setting. |
| */ |
| public boolean getTcpNoDelay() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Boolean) impl.getOption(SocketOptions.TCP_NODELAY); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_KEEPALIVE} option. |
| */ |
| public void setKeepAlive(boolean keepAlive) throws SocketException { |
| if (impl != null) { |
| checkOpenAndCreate(true); |
| impl.setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(keepAlive)); |
| } |
| } |
| |
| /** |
| * 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 { |
| if (factory != null) { |
| throw new SocketException("Factory already set"); |
| } |
| factory = fac; |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}. |
| */ |
| public synchronized void setSendBufferSize(int size) throws SocketException { |
| checkOpenAndCreate(true); |
| if (size < 1) { |
| throw new IllegalArgumentException("size < 1"); |
| } |
| impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}. |
| */ |
| public synchronized void setReceiveBufferSize(int size) throws SocketException { |
| checkOpenAndCreate(true); |
| if (size < 1) { |
| throw new IllegalArgumentException("size < 1"); |
| } |
| impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_LINGER linger} timeout in seconds. |
| * If {@code on} is false, {@code timeout} is irrelevant. |
| */ |
| public void setSoLinger(boolean on, int timeout) throws SocketException { |
| checkOpenAndCreate(true); |
| // The RI explicitly guarantees this idiocy in the SocketOptions.setOption documentation. |
| if (on && timeout < 0) { |
| throw new IllegalArgumentException("timeout < 0"); |
| } |
| if (on) { |
| impl.setOption(SocketOptions.SO_LINGER, Integer.valueOf(timeout)); |
| } else { |
| impl.setOption(SocketOptions.SO_LINGER, Boolean.FALSE); |
| } |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds. |
| * Use 0 for no timeout. |
| * To take effect, this option must be set before the blocking method was called. |
| */ |
| public synchronized void setSoTimeout(int timeout) throws SocketException { |
| checkOpenAndCreate(true); |
| if (timeout < 0) { |
| throw new IllegalArgumentException("timeout < 0"); |
| } |
| impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#TCP_NODELAY} option. |
| */ |
| public void setTcpNoDelay(boolean on) throws SocketException { |
| checkOpenAndCreate(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. |
| */ |
| private void startupSocket(InetAddress dstAddress, int dstPort, |
| InetAddress localAddress, int localPort, boolean streaming) |
| throws IOException { |
| |
| if (localPort < 0 || localPort > 65535) { |
| throw new IllegalArgumentException("Local port out of range: " + localPort); |
| } |
| |
| InetAddress addr = localAddress == null ? Inet6Address.ANY : localAddress; |
| synchronized (this) { |
| impl.create(streaming); |
| isCreated = true; |
| try { |
| if (!streaming || !usingSocks()) { |
| impl.bind(addr, localPort); |
| } |
| isBound = true; |
| cacheLocalAddress(); |
| impl.connect(dstAddress, dstPort); |
| isConnected = true; |
| cacheLocalAddress(); |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| private boolean usingSocks() { |
| return proxy != null && proxy.type() == Proxy.Type.SOCKS; |
| } |
| |
| /** |
| * 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]"; |
| } |
| 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("Socket input is shutdown"); |
| } |
| checkOpenAndCreate(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("Socket output is shutdown"); |
| } |
| checkOpenAndCreate(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 checkOpenAndCreate(boolean create) throws SocketException { |
| if (isClosed()) { |
| throw new SocketException("Socket is closed"); |
| } |
| if (!create) { |
| if (!isConnected()) { |
| throw new SocketException("Socket is not connected"); |
| // 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; |
| } |
| } |
| |
| /** |
| * Returns the local address and port of this socket as a SocketAddress or null if the socket |
| * has never been bound. If the socket is closed but has previously been bound then an address |
| * for which {@link InetAddress#isAnyLocalAddress()} returns true will be returned with the |
| * previously-bound port. This is useful on multihomed hosts. |
| */ |
| public SocketAddress getLocalSocketAddress() { |
| if (!isBound()) { |
| return null; |
| } |
| return new InetSocketAddress(getLocalAddress(), getLocalPort()); |
| } |
| |
| /** |
| * Returns the remote address and port of this socket as a {@code |
| * SocketAddress} or 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 { |
| checkOpenAndCreate(true); |
| if (isBound()) { |
| throw new BindException("Socket is already bound"); |
| } |
| |
| int port; |
| InetAddress addr; |
| if (localAddr == null) { |
| port = 0; |
| addr = Inet6Address.ANY; |
| } else { |
| if (!(localAddr instanceof InetSocketAddress)) { |
| throw new IllegalArgumentException("Local address not an InetSocketAddress: " + |
| localAddr.getClass()); |
| } |
| InetSocketAddress inetAddr = (InetSocketAddress) localAddr; |
| if ((addr = inetAddr.getAddress()) == null) { |
| throw new UnknownHostException("Host is unresolved: " + inetAddr.getHostName()); |
| } |
| port = inetAddr.getPort(); |
| } |
| |
| synchronized (this) { |
| try { |
| impl.bind(addr, port); |
| isBound = true; |
| cacheLocalAddress(); |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Sets the Socket and its related SocketImpl state as if a successful bind() took place, |
| * without actually performing an OS bind(). |
| * |
| * @hide used in java.nio |
| */ |
| public void onBind(InetAddress localAddress, int localPort) { |
| isBound = true; |
| this.localAddress = localAddress; |
| impl.onBind(localAddress, localPort); |
| } |
| |
| /** |
| * 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 { |
| checkOpenAndCreate(true); |
| if (timeout < 0) { |
| throw new IllegalArgumentException("timeout < 0"); |
| } |
| if (isConnected()) { |
| throw new SocketException("Already connected"); |
| } |
| if (remoteAddr == null) { |
| throw new IllegalArgumentException("remoteAddr == null"); |
| } |
| |
| if (!(remoteAddr instanceof InetSocketAddress)) { |
| throw new IllegalArgumentException("Remote address not an InetSocketAddress: " + |
| remoteAddr.getClass()); |
| } |
| InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; |
| InetAddress addr; |
| if ((addr = inetAddr.getAddress()) == null) { |
| throw new UnknownHostException("Host is unresolved: " + inetAddr.getHostName()); |
| } |
| int port = inetAddr.getPort(); |
| |
| checkDestination(addr, port); |
| synchronized (connectLock) { |
| try { |
| if (!isBound()) { |
| // socket already created at this point by earlier call or |
| // checkOpenAndCreate this caused us to lose socket |
| // options on create |
| // impl.create(true); |
| if (!usingSocks()) { |
| impl.bind(Inet6Address.ANY, 0); |
| } |
| isBound = true; |
| } |
| impl.connect(remoteAddr, timeout); |
| isConnected = true; |
| cacheLocalAddress(); |
| } catch (IOException e) { |
| impl.close(); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Sets the Socket and its related SocketImpl state as if a successful connect() took place, |
| * without actually performing an OS connect(). |
| * |
| * @hide internal use only |
| */ |
| public void onConnect(InetAddress remoteAddress, int remotePort) { |
| isConnected = true; |
| impl.onConnect(remoteAddress, remotePort); |
| } |
| |
| /** |
| * 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 this socket's {@link SocketOptions#SO_REUSEADDR} option. |
| */ |
| public void setReuseAddress(boolean reuse) throws SocketException { |
| checkOpenAndCreate(true); |
| impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse)); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_REUSEADDR} setting. |
| */ |
| public boolean getReuseAddress() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Boolean) impl.getOption(SocketOptions.SO_REUSEADDR); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#SO_OOBINLINE} option. |
| */ |
| public void setOOBInline(boolean oobinline) throws SocketException { |
| checkOpenAndCreate(true); |
| impl.setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(oobinline)); |
| } |
| |
| /** |
| * Returns this socket's {@link SocketOptions#SO_OOBINLINE} setting. |
| */ |
| public boolean getOOBInline() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Boolean) impl.getOption(SocketOptions.SO_OOBINLINE); |
| } |
| |
| /** |
| * Sets this socket's {@link SocketOptions#IP_TOS} value for every packet sent by this socket. |
| */ |
| public void setTrafficClass(int value) throws SocketException { |
| checkOpenAndCreate(true); |
| if (value < 0 || value > 255) { |
| throw new IllegalArgumentException("Value doesn't fit in an unsigned byte: " + value); |
| } |
| impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); |
| } |
| |
| /** |
| * Returns this socket's {@see SocketOptions#IP_TOS} setting. |
| */ |
| public int getTrafficClass() throws SocketException { |
| checkOpenAndCreate(true); |
| return (Integer) impl.getOption(SocketOptions.IP_TOS); |
| } |
| |
| /** |
| * 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 { |
| impl.sendUrgentData(value); |
| } |
| |
| /** |
| * Set the appropriate flags for a socket created by {@code |
| * ServerSocket.accept()}. |
| * |
| * @see ServerSocket#implAccept |
| */ |
| void accepted() throws SocketException { |
| isCreated = isBound = isConnected = true; |
| cacheLocalAddress(); |
| } |
| |
| private void cacheLocalAddress() throws SocketException { |
| this.localAddress = IoBridge.getSocketLocalAddress(impl.fd); |
| } |
| |
| /** |
| * Returns this socket's {@code SocketChannel}, if one exists. A channel is |
| * available only if this socket wraps a channel. (That is, you can go from a |
| * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.) |
| * In practice, this means that the socket must have been created by |
| * {@link java.nio.channels.ServerSocketChannel#accept} or |
| * {@link java.nio.channels.SocketChannel#open}. |
| */ |
| public SocketChannel getChannel() { |
| return null; |
| } |
| |
| /** |
| * @hide internal use only |
| */ |
| public FileDescriptor getFileDescriptor$() { |
| return impl.fd; |
| } |
| |
| /** |
| * 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 |
| } |
| } |