PageRenderTime 69ms CodeModel.GetById 2ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 1ms

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

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