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