diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/java/net/MulticastSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/net/MulticastSocket.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,680 @@ +/* + * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.util.Enumeration; + +/** + * The multicast datagram socket class is useful for sending + * and receiving IP multicast packets. A MulticastSocket is + * a (UDP) DatagramSocket, with additional capabilities for + * joining "groups" of other multicast hosts on the internet. + *

+ * A multicast group is specified by a class D IP address + * and by a standard UDP port number. Class D IP addresses + * are in the range 224.0.0.0 to 239.255.255.255, + * inclusive. The address 224.0.0.0 is reserved and should not be used. + *

+ * One would join a multicast group by first creating a MulticastSocket + * with the desired port, then invoking the + * joinGroup(InetAddress groupAddr) + * method: + *

+ * // join a Multicast group and send the group salutations
+ * ...
+ * String msg = "Hello";
+ * InetAddress group = InetAddress.getByName("228.5.6.7");
+ * MulticastSocket s = new MulticastSocket(6789);
+ * s.joinGroup(group);
+ * DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(),
+ *                             group, 6789);
+ * s.send(hi);
+ * // get their responses!
+ * byte[] buf = new byte[1000];
+ * DatagramPacket recv = new DatagramPacket(buf, buf.length);
+ * s.receive(recv);
+ * ...
+ * // OK, I'm done talking - leave the group...
+ * s.leaveGroup(group);
+ * 
+ * + * When one sends a message to a multicast group, all subscribing + * recipients to that host and port receive the message (within the + * time-to-live range of the packet, see below). The socket needn't + * be a member of the multicast group to send messages to it. + *

+ * When a socket subscribes to a multicast group/port, it receives + * datagrams sent by other hosts to the group/port, as do all other + * members of the group and port. A socket relinquishes membership + * in a group by the leaveGroup(InetAddress addr) method. + * Multiple MulticastSocket's may subscribe to a multicast group + * and port concurrently, and they will all receive group datagrams. + *

+ * Currently applets are not allowed to use multicast sockets. + * + * @author Pavani Diwanji + * @since JDK1.1 + */ +public +class MulticastSocket extends DatagramSocket { + /** + * Create a multicast socket. + * + *

If there is a security manager, + * its checkListen method is first called + * with 0 as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + *

+ * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * checkListen method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + */ + public MulticastSocket() throws IOException { + this(new InetSocketAddress(0)); + } + + /** + * Create a multicast socket and bind it to a specific port. + * + *

If there is a security manager, + * its checkListen method is first called + * with the port argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + *

+ * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @param port port to use + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * checkListen method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + */ + public MulticastSocket(int port) throws IOException { + this(new InetSocketAddress(port)); + } + + /** + * Create a MulticastSocket bound to the specified socket address. + *

+ * Or, if the address is null, create an unbound socket. + *

+ *

If there is a security manager, + * its checkListen method is first called + * with the SocketAddress port as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + *

+ * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @param bindaddr Socket address to bind to, or null for + * an unbound socket. + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * checkListen method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + * + * @since 1.4 + */ + public MulticastSocket(SocketAddress bindaddr) throws IOException { + super((SocketAddress) null); + + // Enable SO_REUSEADDR before binding + setReuseAddress(true); + + if (bindaddr != null) { + bind(bindaddr); + } + } + + /** + * The lock on the socket's TTL. This is for set/getTTL and + * send(packet,ttl). + */ + private Object ttlLock = new Object(); + + /** + * The lock on the socket's interface - used by setInterface + * and getInterface + */ + private Object infLock = new Object(); + + /** + * The "last" interface set by setInterface on this MulticastSocket + */ + private InetAddress infAddress = null; + + + /** + * Set the default time-to-live for multicast packets sent out + * on this MulticastSocket in order to control the + * scope of the multicasts. + * + *

The ttl is an unsigned 8-bit quantity, and so must be + * in the range 0 <= ttl <= 0xFF . + * + * @param ttl the time-to-live + * @exception IOException if an I/O exception occurs + * while setting the default time-to-live value + * @deprecated use the setTimeToLive method instead, which uses + * int instead of byte as the type for ttl. + * @see #getTTL() + */ + @Deprecated + public void setTTL(byte ttl) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setTTL(ttl); + } + + /** + * Set the default time-to-live for multicast packets sent out + * on this {@code MulticastSocket} in order to control the + * scope of the multicasts. + * + *

The ttl must be in the range {@code 0 <= ttl <= + * 255} or an {@code IllegalArgumentException} will be thrown. + * Multicast packets sent with a TTL of {@code 0} are not transmitted + * on the network but may be delivered locally. + * + * @param ttl + * the time-to-live + * + * @throws IOException + * if an I/O exception occurs while setting the + * default time-to-live value + * + * @see #getTimeToLive() + */ + public void setTimeToLive(int ttl) throws IOException { + if (ttl < 0 || ttl > 255) { + throw new IllegalArgumentException("ttl out of range"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setTimeToLive(ttl); + } + + /** + * Get the default time-to-live for multicast packets sent out on + * the socket. + * + * @exception IOException if an I/O exception occurs + * while getting the default time-to-live value + * @return the default time-to-live value + * @deprecated use the getTimeToLive method instead, which returns + * an int instead of a byte. + * @see #setTTL(byte) + */ + @Deprecated + public byte getTTL() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return getImpl().getTTL(); + } + + /** + * Get the default time-to-live for multicast packets sent out on + * the socket. + * @exception IOException if an I/O exception occurs while + * getting the default time-to-live value + * @return the default time-to-live value + * @see #setTimeToLive(int) + */ + public int getTimeToLive() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return getImpl().getTimeToLive(); + } + + /** + * Joins a multicast group. Its behavior may be affected by + * setInterface or setNetworkInterface. + * + *

If there is a security manager, this method first + * calls its checkMulticast method + * with the mcastaddr argument + * as its argument. + * + * @param mcastaddr is the multicast address to join + * + * @exception IOException if there is an error joining + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * checkMulticast method doesn't allow the join. + * + * @see SecurityManager#checkMulticast(InetAddress) + */ + public void joinGroup(InetAddress mcastaddr) throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(mcastaddr); + } + + if (!mcastaddr.isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().join(mcastaddr); + } + + /** + * Leave a multicast group. Its behavior may be affected by + * setInterface or setNetworkInterface. + * + *

If there is a security manager, this method first + * calls its checkMulticast method + * with the mcastaddr argument + * as its argument. + * + * @param mcastaddr is the multicast address to leave + * @exception IOException if there is an error leaving + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * checkMulticast method doesn't allow the operation. + * + * @see SecurityManager#checkMulticast(InetAddress) + */ + public void leaveGroup(InetAddress mcastaddr) throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(mcastaddr); + } + + if (!mcastaddr.isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().leave(mcastaddr); + } + + /** + * Joins the specified multicast group at the specified interface. + * + *

If there is a security manager, this method first + * calls its checkMulticast method + * with the mcastaddr argument + * as its argument. + * + * @param mcastaddr is the multicast address to join + * @param netIf specifies the local interface to receive multicast + * datagram packets, or null to defer to the interface set by + * {@link MulticastSocket#setInterface(InetAddress)} or + * {@link MulticastSocket#setNetworkInterface(NetworkInterface)} + * + * @exception IOException if there is an error joining + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * checkMulticast method doesn't allow the join. + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * + * @see SecurityManager#checkMulticast(InetAddress) + * @since 1.4 + */ + public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + + if (oldImpl) + throw new UnsupportedOperationException(); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress()); + } + + if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().joinGroup(mcastaddr, netIf); + } + + /** + * Leave a multicast group on a specified local interface. + * + *

If there is a security manager, this method first + * calls its checkMulticast method + * with the mcastaddr argument + * as its argument. + * + * @param mcastaddr is the multicast address to leave + * @param netIf specifies the local interface or null to defer + * to the interface set by + * {@link MulticastSocket#setInterface(InetAddress)} or + * {@link MulticastSocket#setNetworkInterface(NetworkInterface)} + * @exception IOException if there is an error leaving + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * checkMulticast method doesn't allow the operation. + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * + * @see SecurityManager#checkMulticast(InetAddress) + * @since 1.4 + */ + public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + + if (oldImpl) + throw new UnsupportedOperationException(); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress()); + } + + if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().leaveGroup(mcastaddr, netIf); + } + + /** + * Set the multicast network interface used by methods + * whose behavior would be affected by the value of the + * network interface. Useful for multihomed hosts. + * @param inf the InetAddress + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @see #getInterface() + */ + public void setInterface(InetAddress inf) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + synchronized (infLock) { + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf); + infAddress = inf; + } + } + + /** + * Retrieve the address of the network interface used for + * multicast packets. + * + * @return An InetAddress representing + * the address of the network interface used for + * multicast packets. + * + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * + * @see #setInterface(java.net.InetAddress) + */ + public InetAddress getInterface() throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + synchronized (infLock) { + InetAddress ia = + (InetAddress)getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + + /** + * No previous setInterface or interface can be + * set using setNetworkInterface + */ + if (infAddress == null) { + return ia; + } + + /** + * Same interface set with setInterface? + */ + if (ia.equals(infAddress)) { + return ia; + } + + /** + * Different InetAddress from what we set with setInterface + * so enumerate the current interface to see if the + * address set by setInterface is bound to this interface. + */ + try { + NetworkInterface ni = NetworkInterface.getByInetAddress(ia); + Enumeration addrs = ni.getInetAddresses(); + while (addrs.hasMoreElements()) { + InetAddress addr = (InetAddress)(addrs.nextElement()); + if (addr.equals(infAddress)) { + return infAddress; + } + } + + /** + * No match so reset infAddress to indicate that the + * interface has changed via means + */ + infAddress = null; + return ia; + } catch (Exception e) { + return ia; + } + } + } + + /** + * Specify the network interface for outgoing multicast datagrams + * sent on this socket. + * + * @param netIf the interface + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @see #getNetworkInterface() + * @since 1.4 + */ + public void setNetworkInterface(NetworkInterface netIf) + throws SocketException { + + synchronized (infLock) { + getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf); + infAddress = null; + } + } + + /** + * Get the multicast network interface set. + * + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @return the multicast NetworkInterface currently set + * @see #setNetworkInterface(NetworkInterface) + * @since 1.4 + */ + public NetworkInterface getNetworkInterface() throws SocketException { + NetworkInterface ni + = (NetworkInterface)getImpl().getOption(SocketOptions.IP_MULTICAST_IF2); + if (ni.getIndex() == 0) { + InetAddress[] addrs = new InetAddress[1]; + addrs[0] = InetAddress.anyLocalAddress(); + return new NetworkInterface(addrs[0].getHostName(), 0, addrs); + } else { + return ni; + } + } + + /** + * Disable/Enable local loopback of multicast datagrams + * The option is used by the platform's networking code as a hint + * for setting whether multicast data will be looped back to + * the local socket. + * + *

Because this option is a hint, applications that want to + * verify what loopback mode is set to should call + * {@link #getLoopbackMode()} + * @param disable true to disable the LoopbackMode + * @throws SocketException if an error occurs while setting the value + * @since 1.4 + * @see #getLoopbackMode + */ + public void setLoopbackMode(boolean disable) throws SocketException { + getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable)); + } + + /** + * Get the setting for local loopback of multicast datagrams. + * + * @throws SocketException if an error occurs while getting the value + * @return true if the LoopbackMode has been disabled + * @since 1.4 + * @see #setLoopbackMode + */ + public boolean getLoopbackMode() throws SocketException { + return ((Boolean)getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue(); + } + + /** + * Sends a datagram packet to the destination, with a TTL (time- + * to-live) other than the default for the socket. This method + * need only be used in instances where a particular TTL is desired; + * otherwise it is preferable to set a TTL once on the socket, and + * use that default TTL for all packets. This method does not + * alter the default TTL for the socket. Its behavior may be + * affected by setInterface. + * + *

If there is a security manager, this method first performs some + * security checks. First, if p.getAddress().isMulticastAddress() + * is true, this method calls the + * security manager's checkMulticast method + * with p.getAddress() and ttl as its arguments. + * If the evaluation of that expression is false, + * this method instead calls the security manager's + * checkConnect method with arguments + * p.getAddress().getHostAddress() and + * p.getPort(). Each call to a security manager method + * could result in a SecurityException if the operation is not allowed. + * + * @param p is the packet to be sent. The packet should contain + * the destination multicast ip address and the data to be sent. + * One does not need to be the member of the group to send + * packets to a destination multicast address. + * @param ttl optional time to live for multicast packet. + * default ttl is 1. + * + * @exception IOException is raised if an error occurs i.e + * error while setting ttl. + * @exception SecurityException if a security manager exists and its + * checkMulticast or checkConnect + * method doesn't allow the send. + * + * @deprecated Use the following code or its equivalent instead: + * ...... + * int ttl = mcastSocket.getTimeToLive(); + * mcastSocket.setTimeToLive(newttl); + * mcastSocket.send(p); + * mcastSocket.setTimeToLive(ttl); + * ...... + * + * @see DatagramSocket#send + * @see DatagramSocket#receive + * @see SecurityManager#checkMulticast(java.net.InetAddress, byte) + * @see SecurityManager#checkConnect + */ + @Deprecated + public void send(DatagramPacket p, byte ttl) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + synchronized(ttlLock) { + synchronized(p) { + if (connectState == ST_NOT_CONNECTED) { + // Security manager makes sure that the multicast address + // is allowed one and that the ttl used is less + // than the allowed maxttl. + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (p.getAddress().isMulticastAddress()) { + security.checkMulticast(p.getAddress(), ttl); + } else { + security.checkConnect(p.getAddress().getHostAddress(), + p.getPort()); + } + } + } else { + // we're connected + InetAddress packetAddress = null; + packetAddress = p.getAddress(); + if (packetAddress == null) { + p.setAddress(connectedAddress); + p.setPort(connectedPort); + } else if ((!packetAddress.equals(connectedAddress)) || + p.getPort() != connectedPort) { + throw new SecurityException("connected address and packet address" + + " differ"); + } + } + byte dttl = getTTL(); + try { + if (ttl != dttl) { + // set the ttl + getImpl().setTTL(ttl); + } + // call the datagram method to send + getImpl().send(p); + } finally { + // set it back to default + if (ttl != dttl) { + getImpl().setTTL(dttl); + } + } + } // synch p + } //synch ttl + } //method +}