PageRenderTime 3ms CodeModel.GetById 1349ms app.highlight 447ms RepoModel.GetById 519ms app.codeStats 0ms

/luni/src/main/java/java/net/Socket.java

https://github.com/PAmoto/android_libcore
Java | 1153 lines | 447 code | 75 blank | 631 comment | 71 complexity | 422775a4f7a877ca30ba10f89b8a25f5 MD5 | raw file
   1/*
   2 *  Licensed to the Apache Software Foundation (ASF) under one or more
   3 *  contributor license agreements.  See the NOTICE file distributed with
   4 *  this work for additional information regarding copyright ownership.
   5 *  The ASF licenses this file to You under the Apache License, Version 2.0
   6 *  (the "License"); you may not use this file except in compliance with
   7 *  the License.  You may obtain a copy of the License at
   8 *
   9 *     http://www.apache.org/licenses/LICENSE-2.0
  10 *
  11 *  Unless required by applicable law or agreed to in writing, software
  12 *  distributed under the License is distributed on an "AS IS" BASIS,
  13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 *  See the License for the specific language governing permissions and
  15 *  limitations under the License.
  16 */
  17
  18package java.net;
  19
  20import java.io.IOException;
  21import java.io.InputStream;
  22import java.io.OutputStream;
  23import java.nio.channels.SocketChannel;
  24import org.apache.harmony.luni.net.PlainSocketImpl;
  25import org.apache.harmony.luni.platform.Platform;
  26
  27/**
  28 * Provides a client-side TCP socket.
  29 */
  30public class Socket {
  31    private static SocketImplFactory factory;
  32
  33    final SocketImpl impl;
  34    private final Proxy proxy;
  35
  36    volatile boolean isCreated = false;
  37    private boolean isBound = false;
  38    private boolean isConnected = false;
  39    private boolean isClosed = false;
  40    private boolean isInputShutdown = false;
  41    private boolean isOutputShutdown = false;
  42
  43    private InetAddress localAddress = Inet4Address.ANY;
  44
  45    private static class ConnectLock {
  46    }
  47
  48    private final Object connectLock = new ConnectLock();
  49
  50    /**
  51     * Creates a new unconnected socket. When a SocketImplFactory is defined it
  52     * creates the internal socket implementation, otherwise the default socket
  53     * implementation will be used for this socket.
  54     *
  55     * @see SocketImplFactory
  56     * @see SocketImpl
  57     */
  58    public Socket() {
  59        this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();
  60        this.proxy = null;
  61    }
  62
  63    /**
  64     * Creates a new unconnected socket using the given proxy type. When a
  65     * {@code SocketImplFactory} is defined it creates the internal socket
  66     * implementation, otherwise the default socket implementation will be used
  67     * for this socket.
  68     * <p>
  69     * Example that will create a socket connection through a {@code SOCKS}
  70     * proxy server: <br>
  71     * {@code Socket sock = new Socket(new Proxy(Proxy.Type.SOCKS, new
  72     * InetSocketAddress("test.domain.org", 2130)));}
  73     *
  74     * @param proxy
  75     *            the specified proxy for this socket.
  76     * @throws IllegalArgumentException
  77     *             if the argument {@code proxy} is {@code null} or of an
  78     *             invalid type.
  79     * @throws SecurityException
  80     *             if a security manager exists and it denies the permission to
  81     *             connect to the given proxy.
  82     * @see SocketImplFactory
  83     * @see SocketImpl
  84     */
  85    public Socket(Proxy proxy) {
  86        this.proxy = proxy;
  87        if (proxy == null || proxy.type() == Proxy.Type.HTTP) {
  88            throw new IllegalArgumentException("Proxy is null or invalid type");
  89        }
  90        InetSocketAddress address = (InetSocketAddress) proxy.address();
  91        if (null != address) {
  92            InetAddress addr = address.getAddress();
  93            String host;
  94            if (null != addr) {
  95                host = addr.getHostAddress();
  96            } else {
  97                host = address.getHostName();
  98            }
  99            int port = address.getPort();
 100            checkConnectPermission(host, port);
 101        }
 102        this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl(proxy);
 103    }
 104
 105    // BEGIN android-added
 106    /**
 107     * Tries to connect a socket to all IP addresses of the given hostname.
 108     *
 109     * @param dstName
 110     *            the target host name or IP address to connect to.
 111     * @param dstPort
 112     *            the port on the target host to connect to.
 113     * @param localAddress
 114     *            the address on the local host to bind to.
 115     * @param localPort
 116     *            the port on the local host to bind to.
 117     * @param streaming
 118     *            if {@code true} a streaming socket is returned, a datagram
 119     *            socket otherwise.
 120     * @throws UnknownHostException
 121     *             if the host name could not be resolved into an IP address.
 122     * @throws IOException
 123     *             if an error occurs while creating the socket.
 124     * @throws SecurityException
 125     *             if a security manager exists and it denies the permission to
 126     *             connect to the given address and port.
 127     */
 128    private void tryAllAddresses(String dstName, int dstPort, InetAddress
 129            localAddress, int localPort, boolean streaming) throws IOException {
 130        InetAddress[] dstAddresses = InetAddress.getAllByName(dstName);
 131        // Loop through all the destination addresses except the last, trying to
 132        // connect to each one and ignoring errors. There must be at least one
 133        // address, or getAllByName would have thrown UnknownHostException.
 134        InetAddress dstAddress;
 135        for (int i = 0; i < dstAddresses.length - 1; i++) {
 136            dstAddress = dstAddresses[i];
 137            try {
 138                checkDestination(dstAddress, dstPort);
 139                startupSocket(dstAddress, dstPort, localAddress, localPort, streaming);
 140                return;
 141            } catch (SecurityException e1) {
 142            } catch (IOException e2) {
 143            }
 144        }
 145
 146        // Now try to connect to the last address in the array, handing back to
 147        // the caller any exceptions that are thrown.
 148        dstAddress = dstAddresses[dstAddresses.length - 1];
 149        checkDestination(dstAddress, dstPort);
 150        startupSocket(dstAddress, dstPort, localAddress, localPort, streaming);
 151    }
 152    // END android-added
 153
 154    /**
 155     * Creates a new streaming socket connected to the target host specified by
 156     * the parameters {@code dstName} and {@code dstPort}. The socket is bound
 157     * to any available port on the local host.
 158     * <p><strong>Implementation note:</strong> this implementation tries each
 159     * IP address for the given hostname until it either connects successfully
 160     * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the
 161     * order specified by the system property {@code "java.net.preferIPv6Addresses"}.
 162     *
 163     * @param dstName
 164     *            the target host name or IP address to connect to.
 165     * @param dstPort
 166     *            the port on the target host to connect to.
 167     * @throws UnknownHostException
 168     *             if the host name could not be resolved into an IP address.
 169     * @throws IOException
 170     *             if an error occurs while creating the socket.
 171     * @throws SecurityException
 172     *             if a security manager exists and it denies the permission to
 173     *             connect to the given address and port.
 174     */
 175    public Socket(String dstName, int dstPort) throws UnknownHostException, IOException {
 176        this(dstName, dstPort, null, 0);
 177    }
 178
 179    /**
 180     * Creates a new streaming socket connected to the target host specified by
 181     * the parameters {@code dstName} and {@code dstPort}. On the local endpoint
 182     * the socket is bound to the given address {@code localAddress} on port
 183     * {@code localPort}.
 184     *
 185     * If {@code host} is {@code null} a loopback address is used to connect to.
 186     * <p><strong>Implementation note:</strong> this implementation tries each
 187     * IP address for the given hostname until it either connects successfully
 188     * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the
 189     * order specified by the system property {@code "java.net.preferIPv6Addresses"}.
 190     *
 191     * @param dstName
 192     *            the target host name or IP address to connect to.
 193     * @param dstPort
 194     *            the port on the target host to connect to.
 195     * @param localAddress
 196     *            the address on the local host to bind to.
 197     * @param localPort
 198     *            the port on the local host to bind to.
 199     * @throws UnknownHostException
 200     *             if the host name could not be resolved into an IP address.
 201     * @throws IOException
 202     *             if an error occurs while creating the socket.
 203     * @throws SecurityException
 204     *             if a security manager exists and it denies the permission to
 205     *             connect to the given address and port.
 206     */
 207    public Socket(String dstName, int dstPort, InetAddress localAddress, int localPort) throws IOException {
 208        this();
 209        tryAllAddresses(dstName, dstPort, localAddress, localPort, true);
 210    }
 211
 212    /**
 213     * Creates a new streaming or datagram socket connected to the target host
 214     * specified by the parameters {@code hostName} and {@code port}. The socket
 215     * is bound to any available port on the local host.
 216     * <p><strong>Implementation note:</strong> this implementation tries each
 217     * IP address for the given hostname until it either connects successfully
 218     * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the
 219     * order specified by the system property {@code "java.net.preferIPv6Addresses"}.
 220     *
 221     * @param hostName
 222     *            the target host name or IP address to connect to.
 223     * @param port
 224     *            the port on the target host to connect to.
 225     * @param streaming
 226     *            if {@code true} a streaming socket is returned, a datagram
 227     *            socket otherwise.
 228     * @throws UnknownHostException
 229     *             if the host name could not be resolved into an IP address.
 230     * @throws IOException
 231     *             if an error occurs while creating the socket.
 232     * @throws SecurityException
 233     *             if a security manager exists and it denies the permission to
 234     *             connect to the given address and port.
 235     * @deprecated Use {@code Socket(String, int)} instead of this for streaming
 236     *             sockets or an appropriate constructor of {@code
 237     *             DatagramSocket} for UDP transport.
 238     */
 239    @Deprecated
 240    public Socket(String hostName, int port, boolean streaming) throws IOException {
 241        this();
 242        tryAllAddresses(hostName, port, null, 0, streaming);
 243    }
 244
 245    /**
 246     * Creates a new streaming socket connected to the target host specified by
 247     * the parameters {@code dstAddress} and {@code dstPort}. The socket is
 248     * bound to any available port on the local host.
 249     *
 250     * @param dstAddress
 251     *            the target host address to connect to.
 252     * @param dstPort
 253     *            the port on the target host to connect to.
 254     * @throws IOException
 255     *             if an error occurs while creating the socket.
 256     * @throws SecurityException
 257     *             if a security manager exists and it denies the permission to
 258     *             connect to the given address and port.
 259     */
 260    public Socket(InetAddress dstAddress, int dstPort) throws IOException {
 261        this();
 262        checkDestination(dstAddress, dstPort);
 263        startupSocket(dstAddress, dstPort, null, 0, true);
 264    }
 265
 266    /**
 267     * Creates a new streaming socket connected to the target host specified by
 268     * the parameters {@code dstAddress} and {@code dstPort}. On the local
 269     * endpoint the socket is bound to the given address {@code localAddress} on
 270     * port {@code localPort}.
 271     *
 272     * @param dstAddress
 273     *            the target host address to connect to.
 274     * @param dstPort
 275     *            the port on the target host to connect to.
 276     * @param localAddress
 277     *            the address on the local host to bind to.
 278     * @param localPort
 279     *            the port on the local host to bind to.
 280     * @throws IOException
 281     *             if an error occurs while creating the socket.
 282     * @throws SecurityException
 283     *             if a security manager exists and it denies the permission to
 284     *             connect to the given address and port.
 285     */
 286    public Socket(InetAddress dstAddress, int dstPort,
 287            InetAddress localAddress, int localPort) throws IOException {
 288        this();
 289        checkDestination(dstAddress, dstPort);
 290        startupSocket(dstAddress, dstPort, localAddress, localPort, true);
 291    }
 292
 293    /**
 294     * Creates a new streaming or datagram socket connected to the target host
 295     * specified by the parameters {@code addr} and {@code port}. The socket is
 296     * bound to any available port on the local host.
 297     *
 298     * @param addr
 299     *            the Internet address to connect to.
 300     * @param port
 301     *            the port on the target host to connect to.
 302     * @param streaming
 303     *            if {@code true} a streaming socket is returned, a datagram
 304     *            socket otherwise.
 305     * @throws IOException
 306     *             if an error occurs while creating the socket.
 307     * @throws SecurityException
 308     *             if a security manager exists and it denies the permission to
 309     *             connect to the given address and port.
 310     * @deprecated Use {@code Socket(InetAddress, int)} instead of this for
 311     *             streaming sockets or an appropriate constructor of {@code
 312     *             DatagramSocket} for UDP transport.
 313     */
 314    @Deprecated
 315    public Socket(InetAddress addr, int port, boolean streaming) throws IOException {
 316        this();
 317        checkDestination(addr, port);
 318        startupSocket(addr, port, null, 0, streaming);
 319    }
 320
 321    /**
 322     * Creates an unconnected socket with the given socket implementation.
 323     *
 324     * @param impl
 325     *            the socket implementation to be used.
 326     * @throws SocketException
 327     *             if an error occurs while creating the socket.
 328     */
 329    protected Socket(SocketImpl impl) throws SocketException {
 330        this.impl = impl;
 331        this.proxy = null;
 332    }
 333
 334    /**
 335     * Checks whether the connection destination satisfies the security policy
 336     * and the validity of the port range.
 337     *
 338     * @param destAddr
 339     *            the destination host address.
 340     * @param dstPort
 341     *            the port on the destination host.
 342     */
 343    private void checkDestination(InetAddress destAddr, int dstPort) {
 344        if (dstPort < 0 || dstPort > 65535) {
 345            throw new IllegalArgumentException("Port out of range: " + dstPort);
 346        }
 347        checkConnectPermission(destAddr.getHostAddress(), dstPort);
 348    }
 349
 350    /**
 351     * Checks whether the connection destination satisfies the security policy.
 352     *
 353     * @param hostname
 354     *            the destination hostname.
 355     * @param dstPort
 356     *            the port on the destination host.
 357     */
 358    private void checkConnectPermission(String hostname, int dstPort) {
 359        SecurityManager security = System.getSecurityManager();
 360        if (security != null) {
 361            security.checkConnect(hostname, dstPort);
 362        }
 363    }
 364
 365    /**
 366     * Closes the socket. It is not possible to reconnect or rebind to this
 367     * socket thereafter which means a new socket instance has to be created.
 368     *
 369     * @throws IOException
 370     *             if an error occurs while closing the socket.
 371     */
 372    public synchronized void close() throws IOException {
 373        isClosed = true;
 374        // RI compatibility: the RI returns the any address (but the original local port) after close.
 375        localAddress = Inet4Address.ANY;
 376        impl.close();
 377    }
 378
 379    /**
 380     * Gets the IP address of the target host this socket is connected to.
 381     *
 382     * @return the IP address of the connected target host or {@code null} if
 383     *         this socket is not yet connected.
 384     */
 385    public InetAddress getInetAddress() {
 386        if (!isConnected()) {
 387            return null;
 388        }
 389        return impl.getInetAddress();
 390    }
 391
 392    /**
 393     * Gets an input stream to read data from this socket.
 394     *
 395     * @return the byte-oriented input stream.
 396     * @throws IOException
 397     *             if an error occurs while creating the input stream or the
 398     *             socket is in an invalid state.
 399     */
 400    public InputStream getInputStream() throws IOException {
 401        checkOpenAndCreate(false);
 402        if (isInputShutdown()) {
 403            throw new SocketException("Socket input is shutdown");
 404        }
 405        return impl.getInputStream();
 406    }
 407
 408    /**
 409     * Gets the setting of the socket option {@code SocketOptions.SO_KEEPALIVE}.
 410     *
 411     * @return {@code true} if the {@code SocketOptions.SO_KEEPALIVE} is
 412     *         enabled, {@code false} otherwise.
 413     * @throws SocketException
 414     *             if an error occurs while reading the socket option.
 415     * @see SocketOptions#SO_KEEPALIVE
 416     */
 417    public boolean getKeepAlive() throws SocketException {
 418        checkOpenAndCreate(true);
 419        return (Boolean) impl.getOption(SocketOptions.SO_KEEPALIVE);
 420    }
 421
 422    /**
 423     * Returns the local IP address this socket is bound to, or {@code InetAddress.ANY} if
 424     * the socket is unbound.
 425     */
 426    public InetAddress getLocalAddress() {
 427        return localAddress;
 428    }
 429
 430    /**
 431     * Returns the local port this socket is bound to, or -1 if the socket is unbound.
 432     */
 433    public int getLocalPort() {
 434        if (!isBound()) {
 435            return -1;
 436        }
 437        return impl.getLocalPort();
 438    }
 439
 440    /**
 441     * Gets an output stream to write data into this socket.
 442     *
 443     * @return the byte-oriented output stream.
 444     * @throws IOException
 445     *             if an error occurs while creating the output stream or the
 446     *             socket is in an invalid state.
 447     */
 448    public OutputStream getOutputStream() throws IOException {
 449        checkOpenAndCreate(false);
 450        if (isOutputShutdown()) {
 451            throw new SocketException("Socket output is shutdown");
 452        }
 453        return impl.getOutputStream();
 454    }
 455
 456    /**
 457     * Gets the port number of the target host this socket is connected to.
 458     *
 459     * @return the port number of the connected target host or {@code 0} if this
 460     *         socket is not yet connected.
 461     */
 462    public int getPort() {
 463        if (!isConnected()) {
 464            return 0;
 465        }
 466        return impl.getPort();
 467    }
 468
 469    /**
 470     * Gets the value of the socket option {@link SocketOptions#SO_LINGER}.
 471     *
 472     * @return the current value of the option {@code SocketOptions.SO_LINGER}
 473     *         or {@code -1} if this option is disabled.
 474     * @throws SocketException
 475     *             if an error occurs while reading the socket option.
 476     * @see SocketOptions#SO_LINGER
 477     */
 478    public int getSoLinger() throws SocketException {
 479        checkOpenAndCreate(true);
 480        // The RI explicitly guarantees this idiocy in the SocketOptions.setOption documentation.
 481        Object value = impl.getOption(SocketOptions.SO_LINGER);
 482        if (value instanceof Integer) {
 483            return (Integer) value;
 484        } else {
 485            return -1;
 486        }
 487    }
 488
 489    /**
 490     * Gets the receive buffer size of this socket.
 491     *
 492     * @return the current value of the option {@code SocketOptions.SO_RCVBUF}.
 493     * @throws SocketException
 494     *             if an error occurs while reading the socket option.
 495     * @see SocketOptions#SO_RCVBUF
 496     */
 497    public synchronized int getReceiveBufferSize() throws SocketException {
 498        checkOpenAndCreate(true);
 499        return (Integer) impl.getOption(SocketOptions.SO_RCVBUF);
 500    }
 501
 502    /**
 503     * Gets the send buffer size of this socket.
 504     *
 505     * @return the current value of the option {@code SocketOptions.SO_SNDBUF}.
 506     * @throws SocketException
 507     *             if an error occurs while reading the socket option.
 508     * @see SocketOptions#SO_SNDBUF
 509     */
 510    public synchronized int getSendBufferSize() throws SocketException {
 511        checkOpenAndCreate(true);
 512        return (Integer) impl.getOption(SocketOptions.SO_SNDBUF);
 513    }
 514
 515    /**
 516     * Gets the socket {@link SocketOptions#SO_TIMEOUT receive timeout}.
 517     *
 518     * @throws SocketException
 519     *             if an error occurs while reading the socket option.
 520     */
 521    public synchronized int getSoTimeout() throws SocketException {
 522        checkOpenAndCreate(true);
 523        return (Integer) impl.getOption(SocketOptions.SO_TIMEOUT);
 524    }
 525
 526    /**
 527     * Gets the setting of the socket option {@code SocketOptions.TCP_NODELAY}.
 528     *
 529     * @return {@code true} if the {@code SocketOptions.TCP_NODELAY} is enabled,
 530     *         {@code false} otherwise.
 531     * @throws SocketException
 532     *             if an error occurs while reading the socket option.
 533     * @see SocketOptions#TCP_NODELAY
 534     */
 535    public boolean getTcpNoDelay() throws SocketException {
 536        checkOpenAndCreate(true);
 537        return (Boolean) impl.getOption(SocketOptions.TCP_NODELAY);
 538    }
 539
 540    /**
 541     * Sets the state of the {@code SocketOptions.SO_KEEPALIVE} for this socket.
 542     *
 543     * @param keepAlive
 544     *            the state whether this option is enabled or not.
 545     * @throws SocketException
 546     *             if an error occurs while setting the option.
 547     * @see SocketOptions#SO_KEEPALIVE
 548     */
 549    public void setKeepAlive(boolean keepAlive) throws SocketException {
 550        if (impl != null) {
 551            checkOpenAndCreate(true);
 552            impl.setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(keepAlive));
 553        }
 554    }
 555
 556    /**
 557     * Sets the internal factory for creating socket implementations. This may
 558     * only be executed once during the lifetime of the application.
 559     *
 560     * @param fac
 561     *            the socket implementation factory to be set.
 562     * @throws IOException
 563     *             if the factory has been already set.
 564     */
 565    public static synchronized void setSocketImplFactory(SocketImplFactory fac)
 566            throws IOException {
 567        SecurityManager security = System.getSecurityManager();
 568        if (security != null) {
 569            security.checkSetFactory();
 570        }
 571        if (factory != null) {
 572            throw new SocketException("Factory already set");
 573        }
 574        factory = fac;
 575    }
 576
 577    /**
 578     * Sets the send buffer size of this socket.
 579     *
 580     * @param size
 581     *            the buffer size in bytes. This value must be a positive number
 582     *            greater than {@code 0}.
 583     * @throws SocketException
 584     *             if an error occurs while setting the size or the given value
 585     *             is an invalid size.
 586     * @see SocketOptions#SO_SNDBUF
 587     */
 588    public synchronized void setSendBufferSize(int size) throws SocketException {
 589        checkOpenAndCreate(true);
 590        if (size < 1) {
 591            throw new IllegalArgumentException("size < 1");
 592        }
 593        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
 594    }
 595
 596    /**
 597     * Sets the receive buffer size of this socket.
 598     *
 599     * @param size
 600     *            the buffer size in bytes. This value must be a positive number
 601     *            greater than {@code 0}.
 602     * @throws SocketException
 603     *             if an error occurs while setting the size or the given value
 604     *             is an invalid size.
 605     * @see SocketOptions#SO_RCVBUF
 606     */
 607    public synchronized void setReceiveBufferSize(int size) throws SocketException {
 608        checkOpenAndCreate(true);
 609        if (size < 1) {
 610            throw new IllegalArgumentException("size < 1");
 611        }
 612        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
 613    }
 614
 615    /**
 616     * Sets the {@link SocketOptions#SO_LINGER} timeout in seconds.
 617     *
 618     * @param on
 619     *            the state whether this option is enabled or not.
 620     * @param timeout
 621     *            the linger timeout value in seconds.
 622     * @throws SocketException
 623     *             if an error occurs while setting the option.
 624     * @see SocketOptions#SO_LINGER
 625     */
 626    public void setSoLinger(boolean on, int timeout) throws SocketException {
 627        checkOpenAndCreate(true);
 628        // The RI explicitly guarantees this idiocy in the SocketOptions.setOption documentation.
 629        if (on && timeout < 0) {
 630            throw new IllegalArgumentException("timeout < 0");
 631        }
 632        if (on) {
 633            impl.setOption(SocketOptions.SO_LINGER, Integer.valueOf(timeout));
 634        } else {
 635            impl.setOption(SocketOptions.SO_LINGER, Boolean.FALSE);
 636        }
 637    }
 638
 639    /**
 640     * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket.
 641     * This receive timeout defines the period the socket will block waiting to
 642     * receive data before throwing an {@code InterruptedIOException}. The value
 643     * {@code 0} (default) is used to set an infinite timeout. To have effect
 644     * this option must be set before the blocking method was called.
 645     *
 646     * @param timeout the timeout in milliseconds or 0 for no timeout.
 647     * @throws SocketException
 648     *             if an error occurs while setting the option.
 649     */
 650    public synchronized void setSoTimeout(int timeout) throws SocketException {
 651        checkOpenAndCreate(true);
 652        if (timeout < 0) {
 653            throw new IllegalArgumentException("timeout < 0");
 654        }
 655        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
 656    }
 657
 658    /**
 659     * Sets the state of the {@code SocketOptions.TCP_NODELAY} for this socket.
 660     *
 661     * @param on
 662     *            the state whether this option is enabled or not.
 663     * @throws SocketException
 664     *             if an error occurs while setting the option.
 665     * @see SocketOptions#TCP_NODELAY
 666     */
 667    public void setTcpNoDelay(boolean on) throws SocketException {
 668        checkOpenAndCreate(true);
 669        impl.setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));
 670    }
 671
 672    /**
 673     * Creates a stream socket, binds it to the nominated local address/port,
 674     * then connects it to the nominated destination address/port.
 675     *
 676     * @param dstAddress
 677     *            the destination host address.
 678     * @param dstPort
 679     *            the port on the destination host.
 680     * @param localAddress
 681     *            the address on the local machine to bind.
 682     * @param localPort
 683     *            the port on the local machine to bind.
 684     * @throws IOException
 685     *             thrown if an error occurs during the bind or connect
 686     *             operations.
 687     */
 688    private void startupSocket(InetAddress dstAddress, int dstPort,
 689            InetAddress localAddress, int localPort, boolean streaming)
 690            throws IOException {
 691
 692        if (localPort < 0 || localPort > 65535) {
 693            throw new IllegalArgumentException("Local port out of range: " + localPort);
 694        }
 695
 696        InetAddress addr = localAddress == null ? Inet4Address.ANY : localAddress;
 697        synchronized (this) {
 698            impl.create(streaming);
 699            isCreated = true;
 700            try {
 701                if (!streaming || !usingSocks()) {
 702                    impl.bind(addr, localPort);
 703                }
 704                isBound = true;
 705                impl.connect(dstAddress, dstPort);
 706                isConnected = true;
 707                cacheLocalAddress();
 708            } catch (IOException e) {
 709                impl.close();
 710                throw e;
 711            }
 712        }
 713    }
 714
 715    private boolean usingSocks() {
 716        return proxy != null && proxy.type() == Proxy.Type.SOCKS;
 717    }
 718
 719    /**
 720     * Returns a {@code String} containing a concise, human-readable description of the
 721     * socket.
 722     *
 723     * @return the textual representation of this socket.
 724     */
 725    @Override
 726    public String toString() {
 727        if (!isConnected()) {
 728            return "Socket[unconnected]";
 729        }
 730        return impl.toString();
 731    }
 732
 733    /**
 734     * Closes the input stream of this socket. Any further data sent to this
 735     * socket will be discarded. Reading from this socket after this method has
 736     * been called will return the value {@code EOF}.
 737     *
 738     * @throws IOException
 739     *             if an error occurs while closing the socket input stream.
 740     * @throws SocketException
 741     *             if the input stream is already closed.
 742     */
 743    public void shutdownInput() throws IOException {
 744        if (isInputShutdown()) {
 745            throw new SocketException("Socket input is shutdown");
 746        }
 747        checkOpenAndCreate(false);
 748        impl.shutdownInput();
 749        isInputShutdown = true;
 750    }
 751
 752    /**
 753     * Closes the output stream of this socket. All buffered data will be sent
 754     * followed by the termination sequence. Writing to the closed output stream
 755     * will cause an {@code IOException}.
 756     *
 757     * @throws IOException
 758     *             if an error occurs while closing the socket output stream.
 759     * @throws SocketException
 760     *             if the output stream is already closed.
 761     */
 762    public void shutdownOutput() throws IOException {
 763        if (isOutputShutdown()) {
 764            throw new SocketException("Socket output is shutdown");
 765        }
 766        checkOpenAndCreate(false);
 767        impl.shutdownOutput();
 768        isOutputShutdown = true;
 769    }
 770
 771    /**
 772     * Checks whether the socket is closed, and throws an exception. Otherwise
 773     * creates the underlying SocketImpl.
 774     *
 775     * @throws SocketException
 776     *             if the socket is closed.
 777     */
 778    private void checkOpenAndCreate(boolean create) throws SocketException {
 779        if (isClosed()) {
 780            throw new SocketException("Socket is closed");
 781        }
 782        if (!create) {
 783            if (!isConnected()) {
 784                throw new SocketException("Socket is not connected");
 785                // a connected socket must be created
 786            }
 787
 788            /*
 789             * return directly to fix a possible bug, if !create, should return
 790             * here
 791             */
 792            return;
 793        }
 794        if (isCreated) {
 795            return;
 796        }
 797        synchronized (this) {
 798            if (isCreated) {
 799                return;
 800            }
 801            try {
 802                impl.create(true);
 803            } catch (SocketException e) {
 804                throw e;
 805            } catch (IOException e) {
 806                throw new SocketException(e.toString());
 807            }
 808            isCreated = true;
 809        }
 810    }
 811
 812    /**
 813     * Gets the local address and port of this socket as a SocketAddress or
 814     * {@code null} if the socket is unbound. This is useful on multihomed
 815     * hosts.
 816     *
 817     * @return the bound local socket address and port.
 818     */
 819    public SocketAddress getLocalSocketAddress() {
 820        if (!isBound()) {
 821            return null;
 822        }
 823        return new InetSocketAddress(getLocalAddress(), getLocalPort());
 824    }
 825
 826    /**
 827     * Gets the remote address and port of this socket as a {@code
 828     * SocketAddress} or {@code null} if the socket is not connected.
 829     *
 830     * @return the remote socket address and port.
 831     */
 832    public SocketAddress getRemoteSocketAddress() {
 833        if (!isConnected()) {
 834            return null;
 835        }
 836        return new InetSocketAddress(getInetAddress(), getPort());
 837    }
 838
 839    /**
 840     * Returns whether this socket is bound to a local address and port.
 841     *
 842     * @return {@code true} if the socket is bound to a local address, {@code
 843     *         false} otherwise.
 844     */
 845    public boolean isBound() {
 846        return isBound;
 847    }
 848
 849    /**
 850     * Returns whether this socket is connected to a remote host.
 851     *
 852     * @return {@code true} if the socket is connected, {@code false} otherwise.
 853     */
 854    public boolean isConnected() {
 855        return isConnected;
 856    }
 857
 858    /**
 859     * Returns whether this socket is closed.
 860     *
 861     * @return {@code true} if the socket is closed, {@code false} otherwise.
 862     */
 863    public boolean isClosed() {
 864        return isClosed;
 865    }
 866
 867    /**
 868     * Binds this socket to the given local host address and port specified by
 869     * the SocketAddress {@code localAddr}. If {@code localAddr} is set to
 870     * {@code null}, this socket will be bound to an available local address on
 871     * any free port.
 872     *
 873     * @param localAddr
 874     *            the specific address and port on the local machine to bind to.
 875     * @throws IllegalArgumentException
 876     *             if the given SocketAddress is invalid or not supported.
 877     * @throws IOException
 878     *             if the socket is already bound or an error occurs while
 879     *             binding.
 880     */
 881    public void bind(SocketAddress localAddr) throws IOException {
 882        checkOpenAndCreate(true);
 883        if (isBound()) {
 884            throw new BindException("Socket is already bound");
 885        }
 886
 887        int port = 0;
 888        InetAddress addr = Inet4Address.ANY;
 889        if (localAddr != null) {
 890            if (!(localAddr instanceof InetSocketAddress)) {
 891                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
 892                        localAddr.getClass());
 893            }
 894            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
 895            if ((addr = inetAddr.getAddress()) == null) {
 896                throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
 897            }
 898            port = inetAddr.getPort();
 899        }
 900
 901        synchronized (this) {
 902            try {
 903                impl.bind(addr, port);
 904                isBound = true;
 905                cacheLocalAddress();
 906            } catch (IOException e) {
 907                impl.close();
 908                throw e;
 909            }
 910        }
 911    }
 912
 913    /**
 914     * Connects this socket to the given remote host address and port specified
 915     * by the SocketAddress {@code remoteAddr}.
 916     *
 917     * @param remoteAddr
 918     *            the address and port of the remote host to connect to.
 919     * @throws IllegalArgumentException
 920     *             if the given SocketAddress is invalid or not supported.
 921     * @throws IOException
 922     *             if the socket is already connected or an error occurs while
 923     *             connecting.
 924     */
 925    public void connect(SocketAddress remoteAddr) throws IOException {
 926        connect(remoteAddr, 0);
 927    }
 928
 929    /**
 930     * Connects this socket to the given remote host address and port specified
 931     * by the SocketAddress {@code remoteAddr} with the specified timeout. The
 932     * connecting method will block until the connection is established or an
 933     * error occurred.
 934     *
 935     * @param remoteAddr
 936     *            the address and port of the remote host to connect to.
 937     * @param timeout
 938     *            the timeout value in milliseconds or {@code 0} for an infinite
 939     *            timeout.
 940     * @throws IllegalArgumentException
 941     *             if the given SocketAddress is invalid or not supported or the
 942     *             timeout value is negative.
 943     * @throws IOException
 944     *             if the socket is already connected or an error occurs while
 945     *             connecting.
 946     */
 947    public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
 948        checkOpenAndCreate(true);
 949        if (timeout < 0) {
 950            throw new IllegalArgumentException("timeout < 0");
 951        }
 952        if (isConnected()) {
 953            throw new SocketException("Already connected");
 954        }
 955        if (remoteAddr == null) {
 956            throw new IllegalArgumentException("remoteAddr == null");
 957        }
 958
 959        if (!(remoteAddr instanceof InetSocketAddress)) {
 960            throw new IllegalArgumentException("Remote address not an InetSocketAddress: " +
 961                    remoteAddr.getClass());
 962        }
 963        InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
 964        InetAddress addr;
 965        if ((addr = inetAddr.getAddress()) == null) {
 966            throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
 967        }
 968        int port = inetAddr.getPort();
 969
 970        checkDestination(addr, port);
 971        synchronized (connectLock) {
 972            try {
 973                if (!isBound()) {
 974                    // socket already created at this point by earlier call or
 975                    // checkOpenAndCreate this caused us to lose socket
 976                    // options on create
 977                    // impl.create(true);
 978                    if (!usingSocks()) {
 979                        impl.bind(Inet4Address.ANY, 0);
 980                    }
 981                    isBound = true;
 982                }
 983                impl.connect(remoteAddr, timeout);
 984                isConnected = true;
 985                cacheLocalAddress();
 986            } catch (IOException e) {
 987                impl.close();
 988                throw e;
 989            }
 990        }
 991    }
 992
 993    /**
 994     * Returns whether the incoming channel of the socket has already been
 995     * closed.
 996     *
 997     * @return {@code true} if reading from this socket is not possible anymore,
 998     *         {@code false} otherwise.
 999     */
1000    public boolean isInputShutdown() {
1001        return isInputShutdown;
1002    }
1003
1004    /**
1005     * Returns whether the outgoing channel of the socket has already been
1006     * closed.
1007     *
1008     * @return {@code true} if writing to this socket is not possible anymore,
1009     *         {@code false} otherwise.
1010     */
1011    public boolean isOutputShutdown() {
1012        return isOutputShutdown;
1013    }
1014
1015    /**
1016     * Sets the state of the {@code SocketOptions.SO_REUSEADDR} for this socket.
1017     *
1018     * @param reuse
1019     *            the state whether this option is enabled or not.
1020     * @throws SocketException
1021     *             if an error occurs while setting the option.
1022     * @see SocketOptions#SO_REUSEADDR
1023     */
1024    public void setReuseAddress(boolean reuse) throws SocketException {
1025        checkOpenAndCreate(true);
1026        impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
1027    }
1028
1029    /**
1030     * Gets the setting of the socket option {@code SocketOptions.SO_REUSEADDR}.
1031     *
1032     * @return {@code true} if the {@code SocketOptions.SO_REUSEADDR} is
1033     *         enabled, {@code false} otherwise.
1034     * @throws SocketException
1035     *             if an error occurs while reading the socket option.
1036     * @see SocketOptions#SO_REUSEADDR
1037     */
1038    public boolean getReuseAddress() throws SocketException {
1039        checkOpenAndCreate(true);
1040        return (Boolean) impl.getOption(SocketOptions.SO_REUSEADDR);
1041    }
1042
1043    /**
1044     * Sets the state of the {@code SocketOptions.SO_OOBINLINE} for this socket.
1045     * When this option is enabled urgent data can be received in-line with
1046     * normal data.
1047     *
1048     * @param oobinline
1049     *            whether this option is enabled or not.
1050     * @throws SocketException
1051     *             if an error occurs while setting the option.
1052     * @see SocketOptions#SO_OOBINLINE
1053     */
1054    public void setOOBInline(boolean oobinline) throws SocketException {
1055        checkOpenAndCreate(true);
1056        impl.setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(oobinline));
1057    }
1058
1059    /**
1060     * Gets the setting of the socket option {@code SocketOptions.SO_OOBINLINE}.
1061     *
1062     * @return {@code true} if the {@code SocketOptions.SO_OOBINLINE} is
1063     *         enabled, {@code false} otherwise.
1064     * @throws SocketException
1065     *             if an error occurs while reading the socket option.
1066     * @see SocketOptions#SO_OOBINLINE
1067     */
1068    public boolean getOOBInline() throws SocketException {
1069        checkOpenAndCreate(true);
1070        return (Boolean) impl.getOption(SocketOptions.SO_OOBINLINE);
1071    }
1072
1073    /**
1074     * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket.
1075     *
1076     * @throws SocketException
1077     *             if the socket is closed or the option could not be set.
1078     */
1079    public void setTrafficClass(int value) throws SocketException {
1080        checkOpenAndCreate(true);
1081        if (value < 0 || value > 255) {
1082            throw new IllegalArgumentException();
1083        }
1084        impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
1085    }
1086
1087    /**
1088     * Returns this socket's {@see SocketOptions#IP_TOS} setting.
1089     *
1090     * @throws SocketException
1091     *             if the socket is closed or the option is invalid.
1092     */
1093    public int getTrafficClass() throws SocketException {
1094        checkOpenAndCreate(true);
1095        return (Integer) impl.getOption(SocketOptions.IP_TOS);
1096    }
1097
1098    /**
1099     * Sends the given single byte data which is represented by the lowest octet
1100     * of {@code value} as "TCP urgent data".
1101     *
1102     * @param value
1103     *            the byte of urgent data to be sent.
1104     * @throws IOException
1105     *             if an error occurs while sending urgent data.
1106     */
1107    public void sendUrgentData(int value) throws IOException {
1108        impl.sendUrgentData(value);
1109    }
1110
1111    /**
1112     * Set the appropriate flags for a socket created by {@code
1113     * ServerSocket.accept()}.
1114     *
1115     * @see ServerSocket#implAccept
1116     */
1117    void accepted() {
1118        isCreated = isBound = isConnected = true;
1119        cacheLocalAddress();
1120    }
1121
1122    private void cacheLocalAddress() {
1123        this.localAddress = Platform.getNetworkSystem().getSocketLocalAddress(impl.fd);
1124    }
1125
1126    /**
1127     * Gets the SocketChannel of this socket, if one is available. The current
1128     * implementation of this method returns always {@code null}.
1129     *
1130     * @return the related SocketChannel or {@code null} if no channel exists.
1131     */
1132    public SocketChannel getChannel() {
1133        return null;
1134    }
1135
1136    /**
1137     * Sets performance preferences for connectionTime, latency and bandwidth.
1138     * <p>
1139     * This method does currently nothing.
1140     *
1141     * @param connectionTime
1142     *            the value representing the importance of a short connecting
1143     *            time.
1144     * @param latency
1145     *            the value representing the importance of low latency.
1146     * @param bandwidth
1147     *            the value representing the importance of high bandwidth.
1148     */
1149    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
1150        // Our socket implementation only provide one protocol: TCP/IP, so
1151        // we do nothing for this method
1152    }
1153}