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.IOException;
     21 import java.util.Enumeration;
     22 import libcore.io.IoUtils;
     23 
     24 /**
     25  * This class implements a multicast socket for sending and receiving IP
     26  * multicast datagram packets.
     27  *
     28  * @see DatagramSocket
     29  */
     30 public class MulticastSocket extends DatagramSocket {
     31     /**
     32      * Stores the address supplied to setInterface so we can return it from getInterface. The
     33      * translation to an interface index is lossy because an interface can have multiple addresses.
     34      */
     35     private InetAddress setAddress;
     36 
     37     /**
     38      * Constructs a multicast socket, bound to any available port on the
     39      * local host.
     40      *
     41      * @throws IOException if an error occurs.
     42      */
     43     public MulticastSocket() throws IOException {
     44         setReuseAddress(true);
     45     }
     46 
     47     /**
     48      * Constructs a multicast socket, bound to the specified {@code port} on the
     49      * local host.
     50      *
     51      * @throws IOException if an error occurs.
     52      */
     53     public MulticastSocket(int port) throws IOException {
     54         super(port);
     55         setReuseAddress(true);
     56     }
     57 
     58     /**
     59      * Constructs a {@code MulticastSocket} bound to the address and port specified by
     60      * {@code localAddress}, or an unbound {@code MulticastSocket} if {@code localAddress == null}.
     61      *
     62      * @throws IllegalArgumentException if {@code localAddress} is not supported (because it's not
     63      * an {@code InetSocketAddress}, say).
     64      * @throws IOException if an error occurs.
     65      */
     66     public MulticastSocket(SocketAddress localAddress) throws IOException {
     67         super(localAddress);
     68         setReuseAddress(true);
     69     }
     70 
     71     /**
     72      * Returns an address of the outgoing network interface used by this socket. To avoid
     73      * inherent unpredictability, new code should use {@link #getNetworkInterface} instead.
     74      *
     75      * @throws SocketException if an error occurs.
     76      */
     77     public InetAddress getInterface() throws SocketException {
     78         checkOpen();
     79         if (setAddress != null) {
     80             return setAddress;
     81         }
     82         InetAddress ipvXaddress = (InetAddress) impl.getOption(SocketOptions.IP_MULTICAST_IF);
     83         if (ipvXaddress.isAnyLocalAddress()) {
     84             // the address was not set at the IPv4 level so check the IPv6
     85             // level
     86             NetworkInterface theInterface = getNetworkInterface();
     87             if (theInterface != null) {
     88                 Enumeration<InetAddress> addresses = theInterface.getInetAddresses();
     89                 if (addresses != null) {
     90                     while (addresses.hasMoreElements()) {
     91                         InetAddress nextAddress = addresses.nextElement();
     92                         if (nextAddress instanceof Inet6Address) {
     93                             return nextAddress;
     94                         }
     95                     }
     96                 }
     97             }
     98         }
     99         return ipvXaddress;
    100     }
    101 
    102     /**
    103      * Returns the outgoing network interface used by this socket.
    104      *
    105      * @throws SocketException if an error occurs.
    106      */
    107     public NetworkInterface getNetworkInterface() throws SocketException {
    108         checkOpen();
    109         int index = (Integer) impl.getOption(SocketOptions.IP_MULTICAST_IF2);
    110         if (index != 0) {
    111             return NetworkInterface.getByIndex(index);
    112         }
    113         return NetworkInterface.forUnboundMulticastSocket();
    114     }
    115 
    116     /**
    117      * Returns the time-to-live (TTL) for multicast packets sent on this socket.
    118      *
    119      * @throws IOException if an error occurs.
    120      */
    121     public int getTimeToLive() throws IOException {
    122         checkOpen();
    123         return impl.getTimeToLive();
    124     }
    125 
    126     /**
    127      * Returns the time-to-live (TTL) for multicast packets sent on this socket.
    128      *
    129      * @throws IOException if an error occurs.
    130      * @deprecated Replaced by {@link #getTimeToLive}
    131      */
    132     @Deprecated
    133     public byte getTTL() throws IOException {
    134         checkOpen();
    135         return impl.getTTL();
    136     }
    137 
    138     /**
    139      * Adds this socket to the specified multicast group. A socket must join a
    140      * group before data may be received. A socket may be a member of multiple
    141      * groups but may join any group only once.
    142      *
    143      * @param groupAddr
    144      *            the multicast group to be joined.
    145      * @throws IOException if an error occurs.
    146      */
    147     public void joinGroup(InetAddress groupAddr) throws IOException {
    148         checkJoinOrLeave(groupAddr);
    149         impl.join(groupAddr);
    150     }
    151 
    152     /**
    153      * Adds this socket to the specified multicast group. A socket must join a
    154      * group before data may be received. A socket may be a member of multiple
    155      * groups but may join any group only once.
    156      *
    157      * @param groupAddress
    158      *            the multicast group to be joined.
    159      * @param netInterface
    160      *            the network interface on which the datagram packets will be
    161      *            received.
    162      * @throws IOException
    163      *                if the specified address is not a multicast address.
    164      * @throws IllegalArgumentException
    165      *                if no multicast group is specified.
    166      */
    167     public void joinGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException {
    168         checkJoinOrLeave(groupAddress, netInterface);
    169         impl.joinGroup(groupAddress, netInterface);
    170     }
    171 
    172     /**
    173      * Removes this socket from the specified multicast group.
    174      *
    175      * @param groupAddr
    176      *            the multicast group to be left.
    177      * @throws NullPointerException
    178      *                if {@code groupAddr} is {@code null}.
    179      * @throws IOException
    180      *                if the specified group address is not a multicast address.
    181      */
    182     public void leaveGroup(InetAddress groupAddr) throws IOException {
    183         checkJoinOrLeave(groupAddr);
    184         impl.leave(groupAddr);
    185     }
    186 
    187     /**
    188      * Removes this socket from the specified multicast group.
    189      *
    190      * @param groupAddress
    191      *            the multicast group to be left.
    192      * @param netInterface
    193      *            the network interface on which the addresses should be
    194      *            dropped.
    195      * @throws IOException
    196      *                if the specified group address is not a multicast address.
    197      * @throws IllegalArgumentException
    198      *                if {@code groupAddress} is {@code null}.
    199      */
    200     public void leaveGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException {
    201         checkJoinOrLeave(groupAddress, netInterface);
    202         impl.leaveGroup(groupAddress, netInterface);
    203     }
    204 
    205     private void checkJoinOrLeave(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException {
    206         checkOpen();
    207         if (groupAddress == null) {
    208             throw new IllegalArgumentException("groupAddress == null");
    209         }
    210 
    211         if (netInterface != null && !netInterface.getInetAddresses().hasMoreElements()) {
    212             throw new SocketException("No address associated with interface: " + netInterface);
    213         }
    214 
    215         if (!(groupAddress instanceof InetSocketAddress)) {
    216             throw new IllegalArgumentException("Group address not an InetSocketAddress: " +
    217                     groupAddress.getClass());
    218         }
    219 
    220         InetAddress groupAddr = ((InetSocketAddress) groupAddress).getAddress();
    221         if (groupAddr == null) {
    222             throw new SocketException("Group address has no address: " + groupAddress);
    223         }
    224 
    225         if (!groupAddr.isMulticastAddress()) {
    226             throw new IOException("Not a multicast group: " + groupAddr);
    227         }
    228     }
    229 
    230     private void checkJoinOrLeave(InetAddress groupAddr) throws IOException {
    231         checkOpen();
    232         if (!groupAddr.isMulticastAddress()) {
    233             throw new IOException("Not a multicast group: " + groupAddr);
    234         }
    235     }
    236 
    237     /**
    238      * Sends the given {@code packet} on this socket, using the given {@code ttl}. This method is
    239      * deprecated because it modifies the TTL socket option for this socket twice on each call.
    240      *
    241      * @throws IOException if an error occurs.
    242      * @deprecated use {@link #setTimeToLive}.
    243      */
    244     @Deprecated
    245     public void send(DatagramPacket packet, byte ttl) throws IOException {
    246         checkOpen();
    247         InetAddress packAddr = packet.getAddress();
    248         int currTTL = getTimeToLive();
    249         if (packAddr.isMulticastAddress() && (byte) currTTL != ttl) {
    250             try {
    251                 setTimeToLive(ttl & 0xff);
    252                 impl.send(packet);
    253             } finally {
    254                 setTimeToLive(currTTL);
    255             }
    256         } else {
    257             impl.send(packet);
    258         }
    259     }
    260 
    261     /**
    262      * Sets the outgoing network interface used by this socket. The interface used is the first
    263      * interface found to have the given {@code address}. To avoid inherent unpredictability,
    264      * new code should use {@link #getNetworkInterface} instead.
    265      *
    266      * @throws SocketException if an error occurs.
    267      */
    268     public void setInterface(InetAddress address) throws SocketException {
    269         checkOpen();
    270         if (address == null) {
    271             throw new NullPointerException("address == null");
    272         }
    273 
    274         NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address);
    275         if (networkInterface == null) {
    276             throw new SocketException("Address not associated with an interface: " + address);
    277         }
    278         impl.setOption(SocketOptions.IP_MULTICAST_IF2, networkInterface.getIndex());
    279         this.setAddress = address;
    280     }
    281 
    282     /**
    283      * Sets the outgoing network interface used by this socket to the given
    284      * {@code networkInterface}.
    285      *
    286      * @throws SocketException if an error occurs.
    287      */
    288     public void setNetworkInterface(NetworkInterface networkInterface) throws SocketException {
    289         checkOpen();
    290         if (networkInterface == null) {
    291             throw new SocketException("networkInterface == null");
    292         }
    293 
    294         impl.setOption(SocketOptions.IP_MULTICAST_IF2, networkInterface.getIndex());
    295         this.setAddress = null;
    296     }
    297 
    298     /**
    299      * Sets the time-to-live (TTL) for multicast packets sent on this socket.
    300      * Valid TTL values are between 0 and 255 inclusive.
    301      *
    302      * @throws IOException if an error occurs.
    303      */
    304     public void setTimeToLive(int ttl) throws IOException {
    305         checkOpen();
    306         if (ttl < 0 || ttl > 255) {
    307             throw new IllegalArgumentException("TimeToLive out of bounds: " + ttl);
    308         }
    309         impl.setTimeToLive(ttl);
    310     }
    311 
    312     /**
    313      * Sets the time-to-live (TTL) for multicast packets sent on this socket.
    314      * Valid TTL values are between 0 and 255 inclusive.
    315      *
    316      * @throws IOException if an error occurs.
    317      * @deprecated Replaced by {@link #setTimeToLive}
    318      */
    319     @Deprecated
    320     public void setTTL(byte ttl) throws IOException {
    321         checkOpen();
    322         impl.setTTL(ttl);
    323     }
    324 
    325     @Override
    326     synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
    327         impl = factory != null ? factory.createDatagramSocketImpl() : new PlainDatagramSocketImpl();
    328         impl.create();
    329         try {
    330             impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.TRUE);
    331             impl.bind(aPort, addr);
    332             isBound = true;
    333         } catch (SocketException e) {
    334             close();
    335             throw e;
    336         }
    337     }
    338 
    339     /**
    340      * Returns true if multicast loopback is <i>disabled</i>.
    341      * See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the
    342      * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}.
    343      *
    344      * @throws SocketException if an error occurs.
    345      */
    346     public boolean getLoopbackMode() throws SocketException {
    347         checkOpen();
    348         return !((Boolean) impl.getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue();
    349     }
    350 
    351     /**
    352      * Disables multicast loopback if {@code disable == true}.
    353      * See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the
    354      * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}.
    355      *
    356      * @throws SocketException if an error occurs.
    357      */
    358     public void setLoopbackMode(boolean disable) throws SocketException {
    359         checkOpen();
    360         impl.setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(!disable));
    361     }
    362 }
    363