| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.net; |
| |
| import java.io.IOException; |
| import java.util.Enumeration; |
| import libcore.io.IoUtils; |
| |
| /** |
| * This class implements a multicast socket for sending and receiving IP |
| * multicast datagram packets. |
| * |
| * @see DatagramSocket |
| */ |
| public class MulticastSocket extends DatagramSocket { |
| /** |
| * Stores the address supplied to setInterface so we can return it from getInterface. The |
| * translation to an interface index is lossy because an interface can have multiple addresses. |
| */ |
| private InetAddress setAddress; |
| |
| /** |
| * Constructs a multicast socket, bound to any available port on the |
| * local host. |
| * |
| * @throws IOException if an error occurs. |
| */ |
| public MulticastSocket() throws IOException { |
| setReuseAddress(true); |
| } |
| |
| /** |
| * Constructs a multicast socket, bound to the specified {@code port} on the |
| * local host. |
| * |
| * @throws IOException if an error occurs. |
| */ |
| public MulticastSocket(int port) throws IOException { |
| super(port); |
| setReuseAddress(true); |
| } |
| |
| /** |
| * Constructs a {@code MulticastSocket} bound to the address and port specified by |
| * {@code localAddress}, or an unbound {@code MulticastSocket} if {@code localAddress == null}. |
| * |
| * @throws IllegalArgumentException if {@code localAddress} is not supported (because it's not |
| * an {@code InetSocketAddress}, say). |
| * @throws IOException if an error occurs. |
| */ |
| public MulticastSocket(SocketAddress localAddress) throws IOException { |
| super(localAddress); |
| setReuseAddress(true); |
| } |
| |
| /** |
| * Returns an address of the outgoing network interface used by this socket. To avoid |
| * inherent unpredictability, new code should use {@link #getNetworkInterface} instead. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public InetAddress getInterface() throws SocketException { |
| checkOpen(); |
| if (setAddress != null) { |
| return setAddress; |
| } |
| InetAddress ipvXaddress = (InetAddress) impl.getOption(SocketOptions.IP_MULTICAST_IF); |
| if (ipvXaddress.isAnyLocalAddress()) { |
| // the address was not set at the IPv4 level so check the IPv6 |
| // level |
| NetworkInterface theInterface = getNetworkInterface(); |
| if (theInterface != null) { |
| Enumeration<InetAddress> addresses = theInterface.getInetAddresses(); |
| if (addresses != null) { |
| while (addresses.hasMoreElements()) { |
| InetAddress nextAddress = addresses.nextElement(); |
| if (nextAddress instanceof Inet6Address) { |
| return nextAddress; |
| } |
| } |
| } |
| } |
| } |
| return ipvXaddress; |
| } |
| |
| /** |
| * Returns the outgoing network interface used by this socket. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public NetworkInterface getNetworkInterface() throws SocketException { |
| checkOpen(); |
| int index = (Integer) impl.getOption(SocketOptions.IP_MULTICAST_IF2); |
| if (index != 0) { |
| return NetworkInterface.getByIndex(index); |
| } |
| return NetworkInterface.forUnboundMulticastSocket(); |
| } |
| |
| /** |
| * Returns the time-to-live (TTL) for multicast packets sent on this socket. |
| * |
| * @throws IOException if an error occurs. |
| */ |
| public int getTimeToLive() throws IOException { |
| checkOpen(); |
| return impl.getTimeToLive(); |
| } |
| |
| /** |
| * Returns the time-to-live (TTL) for multicast packets sent on this socket. |
| * |
| * @throws IOException if an error occurs. |
| * @deprecated Replaced by {@link #getTimeToLive} |
| */ |
| @Deprecated |
| public byte getTTL() throws IOException { |
| checkOpen(); |
| return impl.getTTL(); |
| } |
| |
| /** |
| * Adds this socket to the specified multicast group. A socket must join a |
| * group before data may be received. A socket may be a member of multiple |
| * groups but may join any group only once. |
| * |
| * @param groupAddr |
| * the multicast group to be joined. |
| * @throws IOException if an error occurs. |
| */ |
| public void joinGroup(InetAddress groupAddr) throws IOException { |
| checkJoinOrLeave(groupAddr); |
| impl.join(groupAddr); |
| } |
| |
| /** |
| * Adds this socket to the specified multicast group. A socket must join a |
| * group before data may be received. A socket may be a member of multiple |
| * groups but may join any group only once. |
| * |
| * @param groupAddress |
| * the multicast group to be joined. |
| * @param netInterface |
| * the network interface on which the datagram packets will be |
| * received. |
| * @throws IOException |
| * if the specified address is not a multicast address. |
| * @throws IllegalArgumentException |
| * if no multicast group is specified. |
| */ |
| public void joinGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException { |
| checkJoinOrLeave(groupAddress, netInterface); |
| impl.joinGroup(groupAddress, netInterface); |
| } |
| |
| /** |
| * Removes this socket from the specified multicast group. |
| * |
| * @param groupAddr |
| * the multicast group to be left. |
| * @throws NullPointerException |
| * if {@code groupAddr} is {@code null}. |
| * @throws IOException |
| * if the specified group address is not a multicast address. |
| */ |
| public void leaveGroup(InetAddress groupAddr) throws IOException { |
| checkJoinOrLeave(groupAddr); |
| impl.leave(groupAddr); |
| } |
| |
| /** |
| * Removes this socket from the specified multicast group. |
| * |
| * @param groupAddress |
| * the multicast group to be left. |
| * @param netInterface |
| * the network interface on which the addresses should be |
| * dropped. |
| * @throws IOException |
| * if the specified group address is not a multicast address. |
| * @throws IllegalArgumentException |
| * if {@code groupAddress} is {@code null}. |
| */ |
| public void leaveGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException { |
| checkJoinOrLeave(groupAddress, netInterface); |
| impl.leaveGroup(groupAddress, netInterface); |
| } |
| |
| private void checkJoinOrLeave(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException { |
| checkOpen(); |
| if (groupAddress == null) { |
| throw new IllegalArgumentException("groupAddress == null"); |
| } |
| |
| if (netInterface != null && !netInterface.getInetAddresses().hasMoreElements()) { |
| throw new SocketException("No address associated with interface: " + netInterface); |
| } |
| |
| if (!(groupAddress instanceof InetSocketAddress)) { |
| throw new IllegalArgumentException("Group address not an InetSocketAddress: " + |
| groupAddress.getClass()); |
| } |
| |
| InetAddress groupAddr = ((InetSocketAddress) groupAddress).getAddress(); |
| if (groupAddr == null) { |
| throw new SocketException("Group address has no address: " + groupAddress); |
| } |
| |
| if (!groupAddr.isMulticastAddress()) { |
| throw new IOException("Not a multicast group: " + groupAddr); |
| } |
| } |
| |
| private void checkJoinOrLeave(InetAddress groupAddr) throws IOException { |
| checkOpen(); |
| if (!groupAddr.isMulticastAddress()) { |
| throw new IOException("Not a multicast group: " + groupAddr); |
| } |
| } |
| |
| /** |
| * Sends the given {@code packet} on this socket, using the given {@code ttl}. This method is |
| * deprecated because it modifies the TTL socket option for this socket twice on each call. |
| * |
| * @throws IOException if an error occurs. |
| * @deprecated use {@link #setTimeToLive}. |
| */ |
| @Deprecated |
| public void send(DatagramPacket packet, byte ttl) throws IOException { |
| checkOpen(); |
| InetAddress packAddr = packet.getAddress(); |
| int currTTL = getTimeToLive(); |
| if (packAddr.isMulticastAddress() && (byte) currTTL != ttl) { |
| try { |
| setTimeToLive(ttl & 0xff); |
| impl.send(packet); |
| } finally { |
| setTimeToLive(currTTL); |
| } |
| } else { |
| impl.send(packet); |
| } |
| } |
| |
| /** |
| * Sets the outgoing network interface used by this socket. The interface used is the first |
| * interface found to have the given {@code address}. To avoid inherent unpredictability, |
| * new code should use {@link #getNetworkInterface} instead. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public void setInterface(InetAddress address) throws SocketException { |
| checkOpen(); |
| if (address == null) { |
| throw new NullPointerException("address == null"); |
| } |
| |
| NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address); |
| if (networkInterface == null) { |
| throw new SocketException("Address not associated with an interface: " + address); |
| } |
| impl.setOption(SocketOptions.IP_MULTICAST_IF2, networkInterface.getIndex()); |
| this.setAddress = address; |
| } |
| |
| /** |
| * Sets the outgoing network interface used by this socket to the given |
| * {@code networkInterface}. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public void setNetworkInterface(NetworkInterface networkInterface) throws SocketException { |
| checkOpen(); |
| if (networkInterface == null) { |
| throw new SocketException("networkInterface == null"); |
| } |
| |
| impl.setOption(SocketOptions.IP_MULTICAST_IF2, networkInterface.getIndex()); |
| this.setAddress = null; |
| } |
| |
| /** |
| * Sets the time-to-live (TTL) for multicast packets sent on this socket. |
| * Valid TTL values are between 0 and 255 inclusive. |
| * |
| * @throws IOException if an error occurs. |
| */ |
| public void setTimeToLive(int ttl) throws IOException { |
| checkOpen(); |
| if (ttl < 0 || ttl > 255) { |
| throw new IllegalArgumentException("TimeToLive out of bounds: " + ttl); |
| } |
| impl.setTimeToLive(ttl); |
| } |
| |
| /** |
| * Sets the time-to-live (TTL) for multicast packets sent on this socket. |
| * Valid TTL values are between 0 and 255 inclusive. |
| * |
| * @throws IOException if an error occurs. |
| * @deprecated Replaced by {@link #setTimeToLive} |
| */ |
| @Deprecated |
| public void setTTL(byte ttl) throws IOException { |
| checkOpen(); |
| impl.setTTL(ttl); |
| } |
| |
| @Override |
| synchronized void createSocket(int aPort, InetAddress addr) throws SocketException { |
| impl = factory != null ? factory.createDatagramSocketImpl() : new PlainDatagramSocketImpl(); |
| impl.create(); |
| try { |
| impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.TRUE); |
| impl.bind(aPort, addr); |
| isBound = true; |
| } catch (SocketException e) { |
| close(); |
| throw e; |
| } |
| } |
| |
| /** |
| * Returns true if multicast loopback is <i>disabled</i>. |
| * See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the |
| * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public boolean getLoopbackMode() throws SocketException { |
| checkOpen(); |
| return !((Boolean) impl.getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue(); |
| } |
| |
| /** |
| * Disables multicast loopback if {@code disable == true}. |
| * See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the |
| * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}. |
| * |
| * @throws SocketException if an error occurs. |
| */ |
| public void setLoopbackMode(boolean disable) throws SocketException { |
| checkOpen(); |
| impl.setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(!disable)); |
| } |
| } |