001: /*
002: * @(#)MulticastSocket.java 1.47 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.net;
029:
030: import java.io.IOException;
031: import java.io.InterruptedIOException;
032: import java.util.Enumeration;
033:
034: /**
035: * The multicast datagram socket class is useful for sending
036: * and receiving IP multicast packets. A MulticastSocket is
037: * a (UDP) DatagramSocket, with additional capabilities for
038: * joining "groups" of other multicast hosts on the internet.
039: * <P>
040: * A multicast group is specified by a class D IP address
041: * and by a standard UDP port number. Class D IP addresses
042: * are in the range <CODE>224.0.0.0</CODE> to <CODE>239.255.255.255</CODE>,
043: * inclusive. The address 224.0.0.0 is reserved and should not be used.
044: * <P>
045: * One would join a multicast group by first creating a MulticastSocket
046: * with the desired port, then invoking the
047: * <CODE>joinGroup(InetAddress groupAddr)</CODE>
048: * method:
049: * <PRE>
050: * // join a Multicast group and send the group salutations
051: * ...
052: * String msg = "Hello";
053: * InetAddress group = InetAddress.getByName("228.5.6.7");
054: * MulticastSocket s = new MulticastSocket(6789);
055: * s.joinGroup(group);
056: * DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(),
057: * group, 6789);
058: * s.send(hi);
059: * // get their responses!
060: * byte[] buf = new byte[1000];
061: * DatagramPacket recv = new DatagramPacket(buf, buf.length);
062: * s.receive(recv);
063: * ...
064: * // OK, I'm done talking - leave the group...
065: * s.leaveGroup(group);
066: * </PRE>
067: *
068: * When one sends a message to a multicast group, <B>all</B> subscribing
069: * recipients to that host and port receive the message (within the
070: * time-to-live range of the packet, see below). The socket needn't
071: * be a member of the multicast group to send messages to it.
072: * <P>
073: * When a socket subscribes to a multicast group/port, it receives
074: * datagrams sent by other hosts to the group/port, as do all other
075: * members of the group and port. A socket relinquishes membership
076: * in a group by the leaveGroup(InetAddress addr) method. <B>
077: * Multiple MulticastSocket's</B> may subscribe to a multicast group
078: * and port concurrently, and they will all receive group datagrams.
079: * <P>
080: * Currently applets are not allowed to use multicast sockets.
081: *
082: * @author Pavani Diwanji
083: * @since JDK1.1
084: */
085: public class MulticastSocket extends DatagramSocket {
086: /**
087: * Create a multicast socket.
088: *
089: * <p>If there is a security manager,
090: * its <code>checkListen</code> method is first called
091: * with 0 as its argument to ensure the operation is allowed.
092: * This could result in a SecurityException.
093: * <p>
094: * When the socket is created the
095: * {@link DatagramSocket#setReuseAddress(true)} method is
096: * called to enable the SO_REUSEADDR socket option.
097: *
098: * @exception IOException if an I/O exception occurs
099: * while creating the MulticastSocket
100: * @exception SecurityException if a security manager exists and its
101: * <code>checkListen</code> method doesn't allow the operation.
102: * @see SecurityManager#checkListen
103: * @see java.net.DatagramSocket#setReuseAddress(boolean)
104: */
105: public MulticastSocket() throws IOException {
106: this (new InetSocketAddress(0));
107: }
108:
109: /**
110: * Create a multicast socket and bind it to a specific port.
111: *
112: * <p>If there is a security manager,
113: * its <code>checkListen</code> method is first called
114: * with the <code>port</code> argument
115: * as its argument to ensure the operation is allowed.
116: * This could result in a SecurityException.
117: * <p>
118: * When the socket is created the
119: * {@link DatagramSocket#setReuseAddress(true)} method is
120: * called to enable the SO_REUSEADDR socket option.
121: *
122: * @param port port to use
123: * @exception IOException if an I/O exception occurs
124: * while creating the MulticastSocket
125: * @exception SecurityException if a security manager exists and its
126: * <code>checkListen</code> method doesn't allow the operation.
127: * @see SecurityManager#checkListen
128: * @see java.net.DatagramSocket#setReuseAddress(boolean)
129: */
130: public MulticastSocket(int port) throws IOException {
131: this (new InetSocketAddress(port));
132: }
133:
134: /**
135: * Create a MulticastSocket bound to the specified socket address.
136: * <p>
137: * Or, if the address is <code>null</code>, create an unbound socket.
138: * <p>
139: * <p>If there is a security manager,
140: * its <code>checkListen</code> method is first called
141: * with the SocketAddress port as its argument to ensure the operation is allowed.
142: * This could result in a SecurityException.
143: * <p>
144: * When the socket is created the
145: * {@link DatagramSocket#setReuseAddress(true)} method is
146: * called to enable the SO_REUSEADDR socket option.
147: *
148: * @param bindaddr Socket address to bind to, or <code>null</code> for
149: * an unbound socket.
150: * @exception IOException if an I/O exception occurs
151: * while creating the MulticastSocket
152: * @exception SecurityException if a security manager exists and its
153: * <code>checkListen</code> method doesn't allow the operation.
154: * @see SecurityManager#checkListen
155: * @see java.net.DatagramSocket#setReuseAddress(boolean)
156: *
157: * @since 1.4
158: */
159: public MulticastSocket(SocketAddress bindaddr) throws IOException {
160: super ((SocketAddress) null);
161:
162: // Enable SO_REUSEADDR before binding
163: setReuseAddress(true);
164:
165: if (bindaddr != null) {
166: bind(bindaddr);
167: }
168: }
169:
170: /**
171: * The lock on the socket's TTL. This is for set/getTTL and
172: * send(packet,ttl).
173: */
174: private Object ttlLock = new Object();
175:
176: /**
177: * The lock on the socket's interface - used by setInterface
178: * and getInterface
179: */
180: private Object infLock = new Object();
181:
182: /**
183: * The "last" interface set by setInterface on this MulticastSocket
184: */
185: private InetAddress infAddress = null;
186:
187: /**
188: * Set the default time-to-live for multicast packets sent out
189: * on this <code>MulticastSocket</code> in order to control the
190: * scope of the multicasts.
191: *
192: * <p>The ttl is an <b>unsigned</b> 8-bit quantity, and so <B>must</B> be
193: * in the range <code> 0 <= ttl <= 0xFF </code>.
194: *
195: * param ttl the time-to-live
196: * exception IOException if an I/O exception occurs
197: * while setting the default time-to-live value
198: * deprecated use the setTimeToLive method instead, which uses
199: * <b>int</b> instead of <b>byte</b> as the type for ttl.
200: * see #getTTL()
201: *
202: public void setTTL(byte ttl) throws IOException {
203: if (isClosed())
204: throw new SocketException("Socket is closed");
205: getImpl().setTTL(ttl);
206: }
207: */
208:
209: /**
210: * Set the default time-to-live for multicast packets sent out
211: * on this <code>MulticastSocket</code> in order to control the
212: * scope of the multicasts.
213: *
214: * <P> The ttl <B>must</B> be in the range <code> 0 <= ttl <=
215: * 255</code> or an IllegalArgumentException will be thrown.
216: * @exception IOException if an I/O exception occurs
217: * while setting the default time-to-live value
218: * @param ttl the time-to-live
219: * @see #getTimeToLive()
220: */
221: public void setTimeToLive(int ttl) throws IOException {
222: if (ttl < 0 || ttl > 255) {
223: throw new IllegalArgumentException("ttl out of range");
224: }
225: if (isClosed())
226: throw new SocketException("Socket is closed");
227: getImpl().setTimeToLive(ttl);
228: }
229:
230: /**
231: * Get the default time-to-live for multicast packets sent out on
232: * the socket.
233: *
234: * exception IOException if an I/O exception occurs
235: * while getting the default time-to-live value
236: * return the default time-to-live value
237: * deprecated use the getTimeToLive method instead, which returns
238: * an <b>int</b> instead of a <b>byte</b>.
239: * see #setTTL(byte)
240: *
241: public byte getTTL() throws IOException {
242: if (isClosed())
243: throw new SocketException("Socket is closed");
244: return getImpl().getTTL();
245: }
246: */
247:
248: /**
249: * Get the default time-to-live for multicast packets sent out on
250: * the socket.
251: * @exception IOException if an I/O exception occurs while
252: * getting the default time-to-live value
253: * @return the default time-to-live value
254: * @see #setTimeToLive(int)
255: */
256: public int getTimeToLive() throws IOException {
257: if (isClosed())
258: throw new SocketException("Socket is closed");
259: return getImpl().getTimeToLive();
260: }
261:
262: /**
263: * Joins a multicast group. Its behavior may be affected by
264: * <code>setInterface</code> or <code>setNetworkInterface</code>.
265: *
266: * <p>If there is a security manager, this method first
267: * calls its <code>checkMulticast</code> method
268: * with the <code>mcastaddr</code> argument
269: * as its argument.
270: *
271: * @param mcastaddr is the multicast address to join
272: *
273: * @exception IOException if there is an error joining
274: * or when the address is not a multicast address.
275: * @exception SecurityException if a security manager exists and its
276: * <code>checkMulticast</code> method doesn't allow the join.
277: *
278: * @see SecurityManager#checkMulticast(InetAddress)
279: */
280: public void joinGroup(InetAddress mcastaddr) throws IOException {
281: if (isClosed()) {
282: throw new SocketException("Socket is closed");
283: }
284:
285: SecurityManager security = System.getSecurityManager();
286: if (security != null) {
287: security.checkMulticast(mcastaddr);
288: }
289:
290: if (!mcastaddr.isMulticastAddress()) {
291: throw new SocketException("Not a multicast address");
292: }
293:
294: getImpl().join(mcastaddr);
295: }
296:
297: /**
298: * Leave a multicast group. Its behavior may be affected by
299: * <code>setInterface</code> or <code>setNetworkInterface</code>.
300: *
301: * <p>If there is a security manager, this method first
302: * calls its <code>checkMulticast</code> method
303: * with the <code>mcastaddr</code> argument
304: * as its argument.
305: *
306: * @param mcastaddr is the multicast address to leave
307: * @exception IOException if there is an error leaving
308: * or when the address is not a multicast address.
309: * @exception SecurityException if a security manager exists and its
310: * <code>checkMulticast</code> method doesn't allow the operation.
311: *
312: * @see SecurityManager#checkMulticast(InetAddress)
313: */
314: public void leaveGroup(InetAddress mcastaddr) throws IOException {
315: if (isClosed()) {
316: throw new SocketException("Socket is closed");
317: }
318:
319: SecurityManager security = System.getSecurityManager();
320: if (security != null) {
321: security.checkMulticast(mcastaddr);
322: }
323:
324: if (!mcastaddr.isMulticastAddress()) {
325: throw new SocketException("Not a multicast address");
326: }
327:
328: getImpl().leave(mcastaddr);
329: }
330:
331: /**
332: * Joins the specified multicast group at the specified interface.
333: *
334: * <p>If there is a security manager, this method first
335: * calls its <code>checkMulticast</code> method
336: * with the <code>mcastaddr</code> argument
337: * as its argument.
338: *
339: * @param mcastaddr is the multicast address to join
340: * @param netIf specifies the local interface to receive multicast
341: * datagram packets, or <i>null</i> to defer to the interface set by
342: * {@link MulticastSocket#setInterface(InetAddress)} or
343: * {@link MulticastSocket#setNetworkInterface(NetworkInterface)}
344: *
345: * @exception IOException if there is an error joining
346: * or when the address is not a multicast address.
347: * @exception SecurityException if a security manager exists and its
348: * <code>checkMulticast</code> method doesn't allow the join.
349: * @throws IllegalArgumentException if mcastaddr is null or is a
350: * SocketAddress subclass not supported by this socket
351: *
352: * @see SecurityManager#checkMulticast(InetAddress)
353: * @since 1.4
354: */
355: public void joinGroup(SocketAddress mcastaddr,
356: NetworkInterface netIf) throws IOException {
357: if (isClosed())
358: throw new SocketException("Socket is closed");
359:
360: if (mcastaddr == null
361: || !(mcastaddr instanceof InetSocketAddress))
362: throw new IllegalArgumentException(
363: "Unsupported address type");
364:
365: if (oldImpl)
366: throw new UnsupportedOperationException();
367:
368: SecurityManager security = System.getSecurityManager();
369: if (security != null) {
370: security.checkMulticast(((InetSocketAddress) mcastaddr)
371: .getAddress());
372: }
373:
374: if (!((InetSocketAddress) mcastaddr).getAddress()
375: .isMulticastAddress()) {
376: throw new SocketException("Not a multicast address");
377: }
378:
379: getImpl().joinGroup(mcastaddr, netIf);
380: }
381:
382: /**
383: * Leave a multicast group on a specified local interface.
384: *
385: * <p>If there is a security manager, this method first
386: * calls its <code>checkMulticast</code> method
387: * with the <code>mcastaddr</code> argument
388: * as its argument.
389: *
390: * @param mcastaddr is the multicast address to leave
391: * @param netIf specifies the local interface or <i>null</i> to defer
392: * to the interface set by
393: * {@link MulticastSocket#setInterface(InetAddress)} or
394: * {@link MulticastSocket#setNetworkInterface(NetworkInterface)}
395: * @exception IOException if there is an error leaving
396: * or when the address is not a multicast address.
397: * @exception SecurityException if a security manager exists and its
398: * <code>checkMulticast</code> method doesn't allow the operation.
399: * @throws IllegalArgumentException if mcastaddr is null or is a
400: * SocketAddress subclass not supported by this socket
401: *
402: * @see SecurityManager#checkMulticast(InetAddress)
403: * @since 1.4
404: */
405: public void leaveGroup(SocketAddress mcastaddr,
406: NetworkInterface netIf) throws IOException {
407: if (isClosed())
408: throw new SocketException("Socket is closed");
409:
410: if (mcastaddr == null
411: || !(mcastaddr instanceof InetSocketAddress))
412: throw new IllegalArgumentException(
413: "Unsupported address type");
414:
415: if (oldImpl)
416: throw new UnsupportedOperationException();
417:
418: SecurityManager security = System.getSecurityManager();
419: if (security != null) {
420: security.checkMulticast(((InetSocketAddress) mcastaddr)
421: .getAddress());
422: }
423:
424: if (!((InetSocketAddress) mcastaddr).getAddress()
425: .isMulticastAddress()) {
426: throw new SocketException("Not a multicast address");
427: }
428:
429: getImpl().leaveGroup(mcastaddr, netIf);
430: }
431:
432: /**
433: * Set the multicast network interface used by methods
434: * whose behavior would be affected by the value of the
435: * network interface. Useful for multihomed hosts.
436: * @param inf the InetAddress
437: * @exception SocketException if there is an error in
438: * the underlying protocol, such as a TCP error.
439: * @see #getInterface()
440: */
441: public void setInterface(InetAddress inf) throws SocketException {
442: if (isClosed()) {
443: throw new SocketException("Socket is closed");
444: }
445: synchronized (infLock) {
446: getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf);
447: infAddress = inf;
448: }
449: }
450:
451: /**
452: * Retrieve the address of the network interface used for
453: * multicast packets.
454: *
455: * @return An <code>InetAddress</code> representing
456: * the address of the network interface used for
457: * multicast packets.
458: *
459: * @exception SocketException if there is an error in
460: * the underlying protocol, such as a TCP error.
461: *
462: * @see #setInterface(java.net.InetAddress)
463: */
464: public InetAddress getInterface() throws SocketException {
465: if (isClosed()) {
466: throw new SocketException("Socket is closed");
467: }
468: synchronized (infLock) {
469: InetAddress ia = (InetAddress) getImpl().getOption(
470: SocketOptions.IP_MULTICAST_IF);
471:
472: /**
473: * No previous setInterface or interface can be
474: * set using setNetworkInterface
475: */
476: if (infAddress == null) {
477: return ia;
478: }
479:
480: /**
481: * Same interface set with setInterface?
482: */
483: if (ia.equals(infAddress)) {
484: return ia;
485: }
486:
487: /**
488: * Different InetAddress from what we set with setInterface
489: * so enumerate the current interface to see if the
490: * address set by setInterface is bound to this interface.
491: */
492: try {
493: NetworkInterface ni = NetworkInterface
494: .getByInetAddress(ia);
495: Enumeration addrs = ni.getInetAddresses();
496: while (addrs.hasMoreElements()) {
497: InetAddress addr = (InetAddress) (addrs
498: .nextElement());
499: if (addr.equals(infAddress)) {
500: return infAddress;
501: }
502: }
503:
504: /**
505: * No match so reset infAddress to indicate that the
506: * interface has changed via means
507: */
508: infAddress = null;
509: return ia;
510: } catch (Exception e) {
511: return ia;
512: }
513: }
514: }
515:
516: /**
517: * Specify the network interface for outgoing multicast datagrams
518: * sent on this socket.
519: *
520: * @param netIf the interface
521: * @exception SocketException if there is an error in
522: * the underlying protocol, such as a TCP error.
523: * @see #getNetworkInterface()
524: * @since 1.4
525: */
526: public void setNetworkInterface(NetworkInterface netIf)
527: throws SocketException {
528:
529: synchronized (infLock) {
530: getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf);
531: infAddress = null;
532: }
533: }
534:
535: /**
536: * Get the multicast network interface set.
537: *
538: * @exception SocketException if there is an error in
539: * the underlying protocol, such as a TCP error.
540: * @return the multicast <code>NetworkInterface</code> currently set
541: * @see #setNetworkInterface(NetworkInterface)
542: * @since 1.4
543: */
544: public NetworkInterface getNetworkInterface()
545: throws SocketException {
546: NetworkInterface ni = (NetworkInterface) getImpl().getOption(
547: SocketOptions.IP_MULTICAST_IF2);
548: if (ni.getIndex() == 0) {
549: InetAddress[] addrs = new InetAddress[1];
550: addrs[0] = InetAddress.anyLocalAddress();
551: return new NetworkInterface(addrs[0].getHostName(), 0,
552: addrs);
553: } else {
554: return ni;
555: }
556: }
557:
558: /**
559: * Disable/Enable local loopback of multicast datagrams
560: * The option is used by the platform's networking code as a hint
561: * for setting whether multicast data will be looped back to
562: * the local socket.
563: *
564: * <p>Because this option is a hint, applications that want to
565: * verify what loopback mode is set to should call
566: * {@link #getLoopbackMode()}
567: * @param disable <code>true</code> to disable the LoopbackMode
568: * @throws SocketException if an error occurs while setting the value
569: * @since 1.4
570: * @see #getLoopbackMode
571: */
572: public void setLoopbackMode(boolean disable) throws SocketException {
573: getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP,
574: new Boolean(disable));
575: }
576:
577: /**
578: * Get the setting for local loopback of multicast datagrams.
579: *
580: * @throws SocketException if an error occurs while getting the value
581: * @return true if the LoopbackMode has been disabled
582: * @since 1.4
583: * @see #setLoopbackMode
584: */
585: public boolean getLoopbackMode() throws SocketException {
586: return ((Boolean) getImpl().getOption(
587: SocketOptions.IP_MULTICAST_LOOP)).booleanValue();
588: }
589:
590: /**
591: * Sends a datagram packet to the destination, with a TTL (time-
592: * to-live) other than the default for the socket. This method
593: * need only be used in instances where a particular TTL is desired;
594: * otherwise it is preferable to set a TTL once on the socket, and
595: * use that default TTL for all packets. This method does <B>not
596: * </B> alter the default TTL for the socket. Its behavior may be
597: * affected by <code>setInterface</code>.
598: *
599: * <p>If there is a security manager, this method first performs some
600: * security checks. First, if <code>p.getAddress().isMulticastAddress()</code>
601: * is true, this method calls the
602: * security manager's <code>checkMulticast</code> method
603: * with <code>p.getAddress()</code> and <code>ttl</code> as its arguments.
604: * If the evaluation of that expression is false,
605: * this method instead calls the security manager's
606: * <code>checkConnect</code> method with arguments
607: * <code>p.getAddress().getHostAddress()</code> and
608: * <code>p.getPort()</code>. Each call to a security manager method
609: * could result in a SecurityException if the operation is not allowed.
610: *
611: * @param p is the packet to be sent. The packet should contain
612: * the destination multicast ip address and the data to be sent.
613: * One does not need to be the member of the group to send
614: * packets to a destination multicast address.
615: * @param ttl optional time to live for multicast packet.
616: * default ttl is 1.
617: *
618: * @exception IOException is raised if an error occurs i.e
619: * error while setting ttl.
620: * @exception SecurityException if a security manager exists and its
621: * <code>checkMulticast</code> or <code>checkConnect</code>
622: * method doesn't allow the send.
623: *
624: * @deprecated Use the following code or its equivalent instead:
625: * ......
626: * int ttl = mcastSocket.getTimeToLive();
627: * mcastSocket.setTimeToLive(newttl);
628: * mcastSocket.send(p);
629: * mcastSocket.setTimeToLive(ttl);
630: * ......
631: *
632: * @see DatagramSocket#send
633: * @see DatagramSocket#receive
634: * @see SecurityManager#checkMulticast(java.net.InetAddress, byte)
635: * @see SecurityManager#checkConnect
636: */
637: public void send(DatagramPacket p, byte ttl) throws IOException {
638: if (isClosed())
639: throw new SocketException("Socket is closed");
640: synchronized (ttlLock) {
641: synchronized (p) {
642: if (connectState == ST_NOT_CONNECTED) {
643: // Security manager makes sure that the multicast address
644: // is allowed one and that the ttl used is less
645: // than the allowed maxttl.
646: SecurityManager security = System
647: .getSecurityManager();
648: if (security != null) {
649: if (p.getAddress().isMulticastAddress()) {
650: security
651: .checkMulticast(p.getAddress(), ttl);
652: } else {
653: security.checkConnect(p.getAddress()
654: .getHostAddress(), p.getPort());
655: }
656: }
657: } else {
658: // we're connected
659: InetAddress packetAddress = null;
660: packetAddress = p.getAddress();
661: if (packetAddress == null) {
662: p.setAddress(connectedAddress);
663: p.setPort(connectedPort);
664: } else if ((!packetAddress.equals(connectedAddress))
665: || p.getPort() != connectedPort) {
666: throw new SecurityException(
667: "connected address and packet address"
668: + " differ");
669: }
670: }
671: byte dttl = (byte) getTimeToLive();
672: try {
673: if (ttl != dttl) {
674: // set the ttl
675: getImpl().setTimeToLive(ttl);
676: }
677: // call the datagram method to send
678: getImpl().send(p);
679: } finally {
680: // set it back to default
681: if (ttl != dttl) {
682: getImpl().setTimeToLive(dttl);
683: }
684: }
685: } // synch p
686: } //synch ttl
687: } //method
688: }
|