Home | History | Annotate | Download | only in net
      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 
     18 package java.net;
     19 
     20 import java.io.Closeable;
     21 import java.io.FileDescriptor;
     22 import java.io.IOException;
     23 import java.nio.channels.DatagramChannel;
     24 import libcore.io.ErrnoException;
     25 import libcore.io.IoBridge;
     26 import libcore.io.Libcore;
     27 import static libcore.io.OsConstants.*;
     28 
     29 /**
     30  * This class implements a UDP socket for sending and receiving {@code
     31  * DatagramPacket}. A {@code DatagramSocket} object can be used for both
     32  * endpoints of a connection for a packet delivery service.
     33  *
     34  * @see DatagramPacket
     35  * @see DatagramSocketImplFactory
     36  */
     37 public class DatagramSocket implements Closeable {
     38 
     39     DatagramSocketImpl impl;
     40 
     41     InetAddress address;
     42 
     43     int port = -1;
     44 
     45     static DatagramSocketImplFactory factory;
     46 
     47     boolean isBound = false;
     48 
     49     private boolean isConnected = false;
     50 
     51     private SocketException pendingConnectException;
     52 
     53     private boolean isClosed = false;
     54 
     55     private Object lock = new Object();
     56 
     57     /**
     58      * Constructs a UDP datagram socket which is bound to any available port on
     59      * the localhost.
     60      *
     61      * @throws SocketException
     62      *             if an error occurs while creating or binding the socket.
     63      */
     64     public DatagramSocket() throws SocketException {
     65         this(0);
     66     }
     67 
     68     /**
     69      * Constructs a UDP datagram socket which is bound to the specific port
     70      * {@code aPort} on the localhost. Valid values for {@code aPort} are
     71      * between 0 and 65535 inclusive.
     72      *
     73      * @param aPort
     74      *            the port to bind on the localhost.
     75      * @throws SocketException
     76      *             if an error occurs while creating or binding the socket.
     77      */
     78     public DatagramSocket(int aPort) throws SocketException {
     79         checkPort(aPort);
     80         createSocket(aPort, Inet4Address.ANY);
     81     }
     82 
     83     /**
     84      * Constructs a UDP datagram socket which is bound to the specific local
     85      * address {@code addr} on port {@code aPort}. Valid values for {@code
     86      * aPort} are between 0 and 65535 inclusive.
     87      *
     88      * @param aPort
     89      *            the port to bind on the localhost.
     90      * @param addr
     91      *            the address to bind on the localhost.
     92      * @throws SocketException
     93      *             if an error occurs while creating or binding the socket.
     94      */
     95     public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
     96         checkPort(aPort);
     97         createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
     98     }
     99 
    100     private void checkPort(int aPort) {
    101         if (aPort < 0 || aPort > 65535) {
    102             throw new IllegalArgumentException("Port out of range: " + aPort);
    103         }
    104     }
    105 
    106     /**
    107      * Closes this UDP datagram socket and all possibly associated channels.
    108      */
    109     // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is
    110     // noted as not being synchronized.
    111     public void close() {
    112         isClosed = true;
    113         impl.close();
    114     }
    115 
    116     /**
    117      * Disconnects this UDP datagram socket from the remote host. This method
    118      * called on an unconnected socket does nothing.
    119      */
    120     public void disconnect() {
    121         if (isClosed() || !isConnected()) {
    122             return;
    123         }
    124         impl.disconnect();
    125         address = null;
    126         port = -1;
    127         isConnected = false;
    128     }
    129 
    130     synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
    131         impl = factory != null ? factory.createDatagramSocketImpl()
    132                 : new PlainDatagramSocketImpl();
    133         impl.create();
    134         try {
    135             impl.bind(aPort, addr);
    136             isBound = true;
    137         } catch (SocketException e) {
    138             close();
    139             throw e;
    140         }
    141     }
    142 
    143     /**
    144      * Gets the {@code InetAddress} instance representing the remote address to
    145      * which this UDP datagram socket is connected.
    146      *
    147      * @return the remote address this socket is connected to or {@code null} if
    148      *         this socket is not connected.
    149      */
    150     public InetAddress getInetAddress() {
    151         return address;
    152     }
    153 
    154     /**
    155      * Returns the local address to which this socket is bound,
    156      * or {@code null} if this socket is closed.
    157      */
    158     public InetAddress getLocalAddress() {
    159         try {
    160             return IoBridge.getSocketLocalAddress(impl.fd);
    161         } catch (SocketException ex) {
    162             return null;
    163         }
    164     }
    165 
    166     /**
    167      * Gets the local port which this socket is bound to.
    168      *
    169      * @return the local port of this socket or {@code -1} if this socket is
    170      *         closed and {@code 0} if it is unbound.
    171      */
    172     public int getLocalPort() {
    173         if (isClosed()) {
    174             return -1;
    175         }
    176         if (!isBound()) {
    177             return 0;
    178         }
    179         return impl.getLocalPort();
    180     }
    181 
    182     /**
    183      * Gets the remote port which this socket is connected to.
    184      *
    185      * @return the remote port of this socket. The return value {@code -1}
    186      *         indicates that this socket is not connected.
    187      */
    188     public int getPort() {
    189         return port;
    190     }
    191 
    192     /**
    193      * Indicates whether this socket is multicast or not.
    194      *
    195      * @return the return value is always {@code false}.
    196      */
    197     boolean isMulticastSocket() {
    198         return false;
    199     }
    200 
    201     /**
    202      * Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}.
    203      */
    204     public synchronized int getReceiveBufferSize() throws SocketException {
    205         checkOpen();
    206         return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
    207     }
    208 
    209     /**
    210      * Returns this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
    211      */
    212     public synchronized int getSendBufferSize() throws SocketException {
    213         checkOpen();
    214         return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
    215     }
    216 
    217     /**
    218      * Gets the socket {@link SocketOptions#SO_TIMEOUT receive timeout}.
    219      *
    220      * @throws SocketException
    221      *                if an error occurs while getting the option value.
    222      */
    223     public synchronized int getSoTimeout() throws SocketException {
    224         checkOpen();
    225         return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
    226     }
    227 
    228     /**
    229      * Receives a packet from this socket and stores it in the argument {@code
    230      * pack}. All fields of {@code pack} must be set according to the data
    231      * received. If the received data is longer than the packet buffer size it
    232      * is truncated. This method blocks until a packet is received or a timeout
    233      * has expired.
    234      *
    235      * @param pack
    236      *            the {@code DatagramPacket} to store the received data.
    237      * @throws IOException
    238      *                if an error occurs while receiving the packet.
    239      */
    240     public synchronized void receive(DatagramPacket pack) throws IOException {
    241         checkOpen();
    242         ensureBound();
    243         if (pack == null) {
    244             throw new NullPointerException("pack == null");
    245         }
    246         if (pendingConnectException != null) {
    247             throw new SocketException("Pending connect failure", pendingConnectException);
    248         }
    249         pack.resetLengthForReceive();
    250         impl.receive(pack);
    251     }
    252 
    253     /**
    254      * Sends a packet over this socket.
    255      *
    256      * @param pack
    257      *            the {@code DatagramPacket} which has to be sent.
    258      * @throws IOException
    259      *                if an error occurs while sending the packet.
    260      */
    261     public void send(DatagramPacket pack) throws IOException {
    262         checkOpen();
    263         ensureBound();
    264 
    265         InetAddress packAddr = pack.getAddress();
    266         if (address != null) { // The socket is connected
    267             if (packAddr != null) {
    268                 if (!address.equals(packAddr) || port != pack.getPort()) {
    269                     throw new IllegalArgumentException("Packet address mismatch with connected address");
    270                 }
    271             } else {
    272                 pack.setAddress(address);
    273                 pack.setPort(port);
    274             }
    275         } else {
    276             // not connected so the target address is not allowed to be null
    277             if (packAddr == null) {
    278                 throw new NullPointerException("Destination address is null");
    279             }
    280         }
    281         impl.send(pack);
    282     }
    283 
    284     /**
    285      * Sets the network interface used by this socket.  Any packets sent
    286      * via this socket are transmitted via the specified interface.  Any
    287      * packets received by this socket will come from the specified
    288      * interface.  Broadcast datagrams received on this interface will
    289      * be processed by this socket. This corresponds to Linux's SO_BINDTODEVICE.
    290      *
    291      * @hide used by GoogleTV for DHCP
    292      */
    293     public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
    294         if (netInterface == null) {
    295             throw new NullPointerException("netInterface == null");
    296         }
    297         try {
    298             Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
    299         } catch (ErrnoException errnoException) {
    300             throw errnoException.rethrowAsSocketException();
    301         }
    302     }
    303 
    304     /**
    305      * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
    306      */
    307     public synchronized void setSendBufferSize(int size) throws SocketException {
    308         if (size < 1) {
    309             throw new IllegalArgumentException("size < 1");
    310         }
    311         checkOpen();
    312         impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
    313     }
    314 
    315     /**
    316      * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
    317      */
    318     public synchronized void setReceiveBufferSize(int size) throws SocketException {
    319         if (size < 1) {
    320             throw new IllegalArgumentException("size < 1");
    321         }
    322         checkOpen();
    323         impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
    324     }
    325 
    326     /**
    327      * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket.
    328      * This receive timeout defines the period the socket will block waiting to
    329      * receive data before throwing an {@code InterruptedIOException}. The value
    330      * {@code 0} (default) is used to set an infinite timeout. To have effect
    331      * this option must be set before the blocking method was called.
    332      *
    333      * @param timeout the timeout in milliseconds or 0 for no timeout.
    334      * @throws SocketException
    335      *                if an error occurs while setting the option.
    336      */
    337     public synchronized void setSoTimeout(int timeout) throws SocketException {
    338         if (timeout < 0) {
    339             throw new IllegalArgumentException("timeout < 0");
    340         }
    341         checkOpen();
    342         impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
    343     }
    344 
    345     /**
    346      * Sets the socket implementation factory. This may only be invoked once
    347      * over the lifetime of the application. This factory is used to create
    348      * a new datagram socket implementation.
    349      *
    350      * @param fac
    351      *            the socket factory to use.
    352      * @throws IOException
    353      *                if the factory has already been set.
    354      * @see DatagramSocketImplFactory
    355      */
    356     public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
    357             throws IOException {
    358         if (factory != null) {
    359             throw new SocketException("Factory already set");
    360         }
    361         factory = fac;
    362     }
    363 
    364     /**
    365      * Constructs a new {@code DatagramSocket} using the specific datagram
    366      * socket implementation {@code socketImpl}. The created {@code
    367      * DatagramSocket} will not be bound.
    368      *
    369      * @param socketImpl
    370      *            the DatagramSocketImpl to use.
    371      */
    372     protected DatagramSocket(DatagramSocketImpl socketImpl) {
    373         if (socketImpl == null) {
    374             throw new NullPointerException("socketImpl == null");
    375         }
    376         impl = socketImpl;
    377     }
    378 
    379     /**
    380      * Constructs a new {@code DatagramSocket} bound to the host/port specified
    381      * by the {@code SocketAddress} {@code localAddr} or an unbound {@code
    382      * DatagramSocket} if the {@code SocketAddress} is {@code null}.
    383      *
    384      * @param localAddr
    385      *            the local machine address and port to bind to.
    386      * @throws IllegalArgumentException
    387      *             if the SocketAddress is not supported
    388      * @throws SocketException
    389      *             if a problem occurs creating or binding the socket.
    390      */
    391     public DatagramSocket(SocketAddress localAddr) throws SocketException {
    392         if (localAddr != null) {
    393             if (!(localAddr instanceof InetSocketAddress)) {
    394                 throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
    395                         localAddr.getClass());
    396             }
    397             checkPort(((InetSocketAddress) localAddr).getPort());
    398         }
    399         impl = factory != null ? factory.createDatagramSocketImpl()
    400                 : new PlainDatagramSocketImpl();
    401         impl.create();
    402         if (localAddr != null) {
    403             try {
    404                 bind(localAddr);
    405             } catch (SocketException e) {
    406                 close();
    407                 throw e;
    408             }
    409         }
    410         // SocketOptions.SO_BROADCAST is set by default for DatagramSocket
    411         setBroadcast(true);
    412     }
    413 
    414     void checkOpen() throws SocketException {
    415         if (isClosed()) {
    416             throw new SocketException("Socket is closed");
    417         }
    418     }
    419 
    420     private void ensureBound() throws SocketException {
    421         if (!isBound()) {
    422             impl.bind(0, Inet4Address.ANY);
    423             isBound = true;
    424         }
    425     }
    426 
    427     /**
    428      * Binds this socket to the local address and port specified by {@code
    429      * localAddr}. If this value is {@code null} any free port on a valid local
    430      * address is used.
    431      *
    432      * @param localAddr
    433      *            the local machine address and port to bind on.
    434      * @throws IllegalArgumentException
    435      *             if the SocketAddress is not supported
    436      * @throws SocketException
    437      *             if the socket is already bound or a problem occurs during
    438      *             binding.
    439      */
    440     public void bind(SocketAddress localAddr) throws SocketException {
    441         checkOpen();
    442         int localPort = 0;
    443         InetAddress addr = Inet4Address.ANY;
    444         if (localAddr != null) {
    445             if (!(localAddr instanceof InetSocketAddress)) {
    446                 throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
    447                         localAddr.getClass());
    448             }
    449             InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
    450             addr = inetAddr.getAddress();
    451             if (addr == null) {
    452                 throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
    453             }
    454             localPort = inetAddr.getPort();
    455             checkPort(localPort);
    456         }
    457         impl.bind(localPort, addr);
    458         isBound = true;
    459     }
    460 
    461     /**
    462      * Connects this datagram socket to the address and port specified by {@code peer}.
    463      * Future calls to {@link #send} will use this as the default target, and {@link #receive}
    464      * will only accept packets from this source.
    465      *
    466      * @throws SocketException if an error occurs.
    467      */
    468     public void connect(SocketAddress peer) throws SocketException {
    469         if (peer == null) {
    470             throw new IllegalArgumentException("peer == null");
    471         }
    472 
    473         if (!(peer instanceof InetSocketAddress)) {
    474             throw new IllegalArgumentException("peer not an InetSocketAddress: " + peer.getClass());
    475         }
    476 
    477         InetSocketAddress isa = (InetSocketAddress) peer;
    478         if (isa.getAddress() == null) {
    479             throw new SocketException("Host is unresolved: " + isa.getHostName());
    480         }
    481 
    482         synchronized (lock) {
    483             checkOpen();
    484             ensureBound();
    485 
    486             this.address = isa.getAddress();
    487             this.port = isa.getPort();
    488             this.isConnected = true;
    489 
    490             impl.connect(address, port);
    491         }
    492     }
    493 
    494     /**
    495      * Connects this datagram socket to the specific {@code address} and {@code port}.
    496      * Future calls to {@link #send} will use this as the default target, and {@link #receive}
    497      * will only accept packets from this source.
    498      *
    499      * <p>Beware: because it can't throw, this method silently ignores failures.
    500      * Use {@link #connect(SocketAddress)} instead.
    501      */
    502     public void connect(InetAddress address, int port) {
    503         if (address == null) {
    504             throw new IllegalArgumentException("address == null");
    505         }
    506         try {
    507             connect(new InetSocketAddress(address, port));
    508         } catch (SocketException connectException) {
    509             // TODO: or just use SneakyThrow? There's a clear API bug here.
    510             pendingConnectException = connectException;
    511         }
    512     }
    513 
    514     /**
    515      * Returns true if this socket is bound to a local address. See {@link #bind}.
    516      */
    517     public boolean isBound() {
    518         return isBound;
    519     }
    520 
    521     /**
    522      * Returns true if this datagram socket is connected to a remote address. See {@link #connect}.
    523      */
    524     public boolean isConnected() {
    525         return isConnected;
    526     }
    527 
    528     /**
    529      * Returns the {@code SocketAddress} this socket is connected to, or null for an unconnected
    530      * socket.
    531      */
    532     public SocketAddress getRemoteSocketAddress() {
    533         if (!isConnected()) {
    534             return null;
    535         }
    536         return new InetSocketAddress(getInetAddress(), getPort());
    537     }
    538 
    539     /**
    540      * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket.
    541      */
    542     public SocketAddress getLocalSocketAddress() {
    543         if (!isBound()) {
    544             return null;
    545         }
    546         return new InetSocketAddress(getLocalAddress(), getLocalPort());
    547     }
    548 
    549     /**
    550      * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option
    551      * has to be enabled if more than one UDP socket wants to be bound to the
    552      * same address. That could be needed for receiving multicast packets.
    553      * <p>
    554      * There is an undefined behavior if this option is set after the socket is
    555      * already bound.
    556      *
    557      * @param reuse
    558      *            the socket option value to enable or disable this option.
    559      * @throws SocketException
    560      *             if the socket is closed or the option could not be set.
    561      */
    562     public void setReuseAddress(boolean reuse) throws SocketException {
    563         checkOpen();
    564         impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
    565     }
    566 
    567     /**
    568      * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}.
    569      *
    570      * @return {@code true} if the option is enabled, {@code false} otherwise.
    571      * @throws SocketException
    572      *             if the socket is closed or the option is invalid.
    573      */
    574     public boolean getReuseAddress() throws SocketException {
    575         checkOpen();
    576         return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue();
    577     }
    578 
    579     /**
    580      * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option
    581      * must be enabled to send broadcast messages.
    582      *
    583      * @param broadcast
    584      *            the socket option value to enable or disable this option.
    585      * @throws SocketException
    586      *             if the socket is closed or the option could not be set.
    587      */
    588     public void setBroadcast(boolean broadcast) throws SocketException {
    589         checkOpen();
    590         impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast));
    591     }
    592 
    593     /**
    594      * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}.
    595      *
    596      * @return {@code true} if the option is enabled, {@code false} otherwise.
    597      * @throws SocketException
    598      *             if the socket is closed or the option is invalid.
    599      */
    600     public boolean getBroadcast() throws SocketException {
    601         checkOpen();
    602         return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST)).booleanValue();
    603     }
    604 
    605     /**
    606      * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket.
    607      *
    608      * @throws SocketException
    609      *             if the socket is closed or the option could not be set.
    610      */
    611     public void setTrafficClass(int value) throws SocketException {
    612         checkOpen();
    613         if (value < 0 || value > 255) {
    614             throw new IllegalArgumentException("Value doesn't fit in an unsigned byte: " + value);
    615         }
    616         impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
    617     }
    618 
    619     /**
    620      * Returns this socket's {@see SocketOptions#IP_TOS} setting.
    621      *
    622      * @throws SocketException
    623      *             if the socket is closed or the option is invalid.
    624      */
    625     public int getTrafficClass() throws SocketException {
    626         checkOpen();
    627         return (Integer) impl.getOption(SocketOptions.IP_TOS);
    628     }
    629 
    630     /**
    631      * Gets the state of this socket.
    632      *
    633      * @return {@code true} if the socket is closed, {@code false} otherwise.
    634      */
    635     public boolean isClosed() {
    636         return isClosed;
    637     }
    638 
    639     /**
    640      * Returns this socket's {@code DatagramChannel}, if one exists. A channel is
    641      * available only if this socket wraps a channel. (That is, you can go from a
    642      * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.)
    643      * In practice, this means that the socket must have been created by
    644      * {@link java.nio.channels.DatagramChannel#open}.
    645      */
    646     public DatagramChannel getChannel() {
    647         return null;
    648     }
    649 
    650     /**
    651      * @hide internal use only
    652      */
    653     public final FileDescriptor getFileDescriptor$() {
    654         return impl.fd;
    655     }
    656 }
    657