|    Enough theory. In Java, you multicast data using the  java.net.MulticastSocket  class, a subclass of  java.net.DatagramSocket  :    public class MulticastSocket extends DatagramSocket      As you would expect,  MulticastSocket  's behavior is very similar to  DatagramSocket  's: you put your data in  DatagramPacket  objects that you send and receive with the  MulticastSocket  . Therefore, I won't repeat the basics; this discussion assumes that you already know how to work with datagrams. If you're jumping around in this book rather than reading it cover to cover, now might be a good time to go back and read Chapter 13 on UDP.    To receive data that is being multicast from a remote site, first create a  MulticastSocket  with the  MulticastSocket( )  constructor. Next, join a multicast group using the  MulticastSocket  's  joinGroup( )  method. This signals the routers in the path between you and the server to start sending data your way and tells the local host that it should pass you IP packets addressed to the multicast group.    Once you've joined the multicast group, you receive UDP data just as you would with a  DatagramSocket  . That is, you create a  DatagramPacket  with a byte array that serves as a buffer for data and enter a loop in which you receive the data by calling the  receive( )  method inherited from the  DatagramSocket  class. When you no longer want to receive data, leave the multicast group by invoking the socket's  leaveGroup()  method. You can then close the socket with the  close( )  method inherited from  DatagramSocket  .    Sending data to a multicast address is similar to sending UDP data to a unicast address. You do not need to join a multicast group to send data to it. You create a new  DatagramPacket  , stuff the data and the address of the multicast group into the packet, and pass it to the  send( )  method. The one difference is that you must explicitly specify the packet's TTL value.    There is one caveat to all this: multicast sockets are a security hole big enough to drive a small truck through. Consequently, untrusted code running under the control of a  SecurityManager  is not allowed to do anything involving multicast sockets. Remotely loaded code is normally allowed to send datagrams to or receive datagrams from the host it was downloaded from. However, multicast sockets don't allow this sort of restriction to be placed on the packets they send or receive. Once you send data to a multicast socket, you have very limited and unreliable control over which hosts receive that data. Consequently, most environments that execute remote code take the conservative approach of disallowing all multicasting.    14.2.1 The Constructors   The constructors are simple. Each one calls the equivalent constructor in the  DatagramSocket  superclass.    14.2.1.1 public MulticastSocket( ) throws SocketException   This constructor creates a socket that is bound to an anonymous port (i.e., an unused port assigned by the system). It is useful for clients (i.e., programs that initiate a data transfer) because they don't need to use a well-known port: the recipient replies to the port contained in the packet. If you need to know the port number, look it up with the  getLocalPort( )  method inherited from  DatagramSocket  . This constructor throws a  SocketException  if the  Socket  can't be created. For example:    try {   MulticastSocket ms = new MulticastSocket( );   // send some datagrams... } catch (SocketException se) {   System.err.println(se); }     14.2.1.2 public MulticastSocket(int port) throws SocketException   This constructor creates a socket that receives datagrams on a well-known port. The  port  argument specifies the port on which this socket listens for datagrams. As with regular TCP and UDP unicast sockets, on a Unix system a program needs to be run with root privileges in order to create a  MulticastSocket  on a port numbered from 1 to 1,023.    This constructor throws a  SocketException  if the  Socket  can't be created. A  Socket  can't be created if you don't have sufficient privileges to bind to the port or if the port you're trying to bind to is already in use. Note that since a multicast socket is a datagram socket as far as the operating system is concerned , a  MulticastSocket  cannot occupy a port already occupied by a  DatagramSocket  , and vice versa. For example, this code fragment opens a multicast socket on port 4,000:    try {   MulticastSocket ms = new MulticastSocket(4000);   // receive incoming datagrams... } catch (SocketException ex) {   System.err.println(ex); }     14.2.1.3 public MulticastSocket(SocketAddress bindAddress) throws IOException // Java 1.4   Starting in Java 1.4, you can create a  MulticastSocket  using a  SocketAddress  object. If the  SocketAddress  is bound to a port, then this is pretty much the same as the previous constructor. For example, this code fragment also opens a  MulticastSocket  on port 4000 that listens on all network interfaces and addresses:    try {   SocketAddress address = new InetSocketAddress(4000);   MulticastSocket ms = new MulticastSocket(address);   // receive incoming datagrams... } catch (SocketException ex) {   System.err.println(ex); }     However, the  SocketAddress  can also be bound to a specific network interface on the local host, rather than listening on all network interfaces. For example, this code fragment also opens a  MulticastSocket  on port 4000 that only listens to packets arriving on 192.168.254.32:    try {   SocketAddress address = new InetSocketAddress("192.168.254.32", 4000);   MulticastSocket ms = new MulticastSocket(address);   // receive incoming datagrams... } catch (SocketException ex) {   System.err.println(ex); }     Finally, you can pass null to this constructor to create an unbound socket, which would later be connected with the  bind()  method. This is useful when setting socket options that can only be set before the socket is bound. For example, this code fragment creates a multicast socket with SO_REUSEADDR disabled (that option is normally enabled by default for multicast sockets):    try {   MulticastSocket ms = new MulticastSocket(null);   ms.setReuseAddress(false);   SocketAddress address = new InetSocketAddress(4000);   ms.bind(address);   // receive incoming datagrams... } catch (SocketException ex) {   System.err.println(ex); }     14.2.2 Communicating with a Multicast Group   Once a  MulticastSocket  has been created, it can perform four key operations:     -   
  -  
  Send data to the members of the group.       -  
  Receive data from the group.       -  
  Leave the multicast group.            The  MulticastSocket  class has methods for operations 1, 2, and 4. No new method is required to receive data. The  receive( )  method of the superclass,  DatagramSocket  , suffices for this task. You can perform these operations in any order, with the exception that you must join a group before you can receive data from it (or, for that matter, leave it). You do not need to join a group to send data to it, and the sending and receiving of data may be freely interwoven.    14.2.2.1 public void joinGroup(InetAddress address) throws IOException   To receive data from a  MulticastSocket  , you must first join a multicast group. To join a group, pass an  InetAddress  object for the multicast group to the  joinGroup( )  method. If you successfully join the group, you'll receive any datagrams intended for that group. Once you've joined a multicast group, you receive datagrams exactly as you receive unicast datagrams, as shown in the previous chapter. That is, you set up a  DatagramPacket  as a buffer and pass it into this socket's  receive( )  method. For example:    try {   MulticastSocket ms = new MulticastSocket(4000);   InetAddress ia = InetAddress.getByName("224.2.2.2");   ms.joinGroup(ia);   byte[] buffer = new byte[8192];   while (true) {     DatagramPacket dp = new DatagramPacket(buffer, buffer.length);     ms.receive(dp);     String s = new String(dp.getData( ), "8859_1");     System.out.println(s);   } } catch (IOException ex) {   System.err.println(ex); }     If the address that you try to join is not a multicast address (that is, it is not between 224.0.0.0 and 239.255.255.255), the  joinGroup( )  method throws an  IOException  .    A single  MulticastSocket  can join multiple multicast groups. Information about membership in multicast groups is stored in multicast routers, not in the object. In this case, you'd use the address stored in the incoming datagram to determine which address a packet was intended for.    Multiple multicast sockets on the same machine and even in the same Java program can all join the same group. If so, they'll all receive all data addressed to that group that arrives at the local host.    14.2.2.2 public void joinGroup(SocketAddress address, NetworkInterface interface) throws IOException // Java 1.4   Java 1.4 adds this overloaded variant of  joinGroup()  that allows you to join a multicast group only on a specified local network interface. A proxy server or firewall might use this to specify that it will accept multicast data from the interface connected to the LAN, but not the interface connected to the global Internet, for instance.    For example, this code fragment attempts to join the group with IP address 224.2.2.2 on the network interface named "eth0", if such an interface exists. If no such interface exists, then it joins on all available network interfaces:    MulticastSocket ms = new MulticastSocket(4000); SocketAddress group = new InetSocketAddress("224.2.2.2", 40); NetworkInterface ni = NetworkInterface .getByName("eth0"); if (ni != null) {   ms.joinGroup(group, ni); } else {   ms.joinGroup(group); }     Other than the extra argument specifying the network interface to listen from, this behaves pretty much like the single argument  joinGroup( )  method. For instance, passing a  SocketAddress  object that does not represent a multicast group as the first argument throws an  IOException  .    14.2.2.3 public void leaveGroup(InetAddress address) throws IOException   The  leaveGroup( )  method signals that you no longer want to receive datagrams from the specified multicast group. A signal is sent to the appropriate multicast router, telling it to stop sending you datagrams. If the address you try to leave is not a multicast address (that is, if it is not between 224.0.0.0 and 239.255.255.255), the method throws an  IOException  . However, no exception occurs if you leave a multicast group you never joined.    14.2.2.4 public void leaveGroup(SocketAddress multicastAddress, NetworkInterface interface) throws IOException // Java 1.4   Java 1.4 also allows you to specify that you no longer want to receive datagrams on one particular network interface. Perhaps you do wish to continue receiving datagrams on other network interfaces. For instance, you could join on all interfaces, and then leave just one. To be honest, this is a bit of a stretch. This method was probably included mostly for symmetry with  joinGroup( )  .    14.2.2.5 public void send(DatagramPacket packet, byte ttl) throws IOException   Sending data with a  MulticastSocket  is similar to sending data with a  DatagramSocket  . Stuff your data into a  DatagramPacket  object and send it off using the  send( )  method inherited from  DatagramSocket  :    public void send(DatagramPacket p) throws IOException      The data is sent to every host that belongs to the multicast group to which the packet is addressed. For example:    try {   InetAddress ia = InetAddress.getByName("experiment.mcast.net");   byte[] data = "Here's some multicast data\r\n".getBytes( );   int port = 4000;   DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);     MulticastSocket ms = new MulticastSocket( );   ms.send(dp); } catch (IOException ex) {   System.err.println(ex); }     However, the  MulticastSocket  class adds an overloaded variant of the  send( )  method that lets you provide a value for the Time-To-Live field  ttl  . By default, the  send( )  method uses a TTL of 1; that is, packets don't travel outside the local subnet. However, you can change this setting for an individual packet by passing an integer from 0 to 255 as the second argument to the  send()  method. For example:    DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);     MulticastSocket ms = new MulticastSocket( );   ms.send(dp, 64);      14.2.2.6 public void setInterface(InetAddress address) throws SocketException   On a multihomed host, the  setInterface()  method chooses the network interface used for multicast sending and receiving.  setInterface( )  throws a  SocketException  if the  InetAddress  argument is not the address of a network interface on the local machine. It is unclear why the network interface is immutably set in the constructor for unicast  Socket  and  DatagramSocket  objects but is variable and set with a separate method for  MulticastSocket  objects. To be safe, set the interface immediately after constructing a  MulticastSocket  and don't change it thereafter. Here's how you might use  setInterface( )  :    MulticastSocket ms; InetAddress ia; try {   ia = InetAddress.getByName("www.ibiblio.org");   ms = new MulticastSocket(2048);   ms.setInterface(ia);   // send and receive data... } catch (UnknownHostException ue) {   System.err.println(ue); } catch (SocketException se) {   System.err.println(se); }     14.2.2.7 public InetAddress getInterface( ) throws SocketException   If you need to know the address of the interface the socket is bound to, call  getInterface()  . It isn't clear why this method would throw an exception; in any case, you must be prepared for it. For example:    try {   MulticastSocket ms = new MulticastSocket(2048);   InetAddress ia = ms.getInterface( ); } catch (SocketException se) {   System.err.println(ue); }     14.2.2.8 public void setNetworkInterface(NetworkInterface interface) throws SocketException // Java 1.4   The  setNetworkInterface()  method serves the same purpose as the  setInterface( )  method; that is, it chooses the network interface used for multicast sending and receiving. However, it does so based on the local name of a network interface such as "eth0" (as encapsulated in a  NetworkInterface  object) rather than on the IP address bound to that network interface (as encapsulated in an  InetAddress  object).  setNetworkInterface()  throws a  SocketException  if the  NetworkInterface  passed as an argument is not a network interface on the local machine.    14.2.2.9 public NetworkInterface getNetworkInterface( ) throws SocketException // Java 1.4   The  getNetworkInterface()  method returns a  NetworkInterface  object representing the network interface on which this  MulticastSocket  is listening for data. If no network interface has been explicitly set in the constructor or with  setNetworkInterface( )  , it returns a placeholder object with the address "0.0.0.0" and the index -1. For example, this code fragment prints the network interface used by a socket:    NetworkInterface intf = ms.getNetworkInterface( ); System.out.println(intf.getName( ));      14.2.2.10 public void setTimeToLive(int ttl) throws IOException // Java 1.2   The  setTimeToLive()  method sets the default TTL value used for packets sent from the socket using the  send(Datagrampacket dp)  method inherited from  DatagramSocket  (as opposed to the  send(Datagrampacket   dp  ,  byte   ttl)  method in  MulticastSocket  ). This method is only available in Java 1.2 and later. In Java 1.1, you have to use the  setTTL( )  method instead:    public void setTTL(byte ttl) throws IOException      The  setTTL( )  method is deprecated in Java 2 and later because it only allows TTL values from 1 to 127 rather than the full range from 1 to 255.    14.2.2.11 public int getTimeToLive( ) throws IOException // Java 1.2   The  getTimeToLive()  method returns the default TTL value of the  MulticastSocket  . It's not needed very much. This method is also available only in Java 1.2 and later. In Java 1.1, you have to use the  getTTL( )  method instead:    public byte getTTL( ) throws IOException      The  getTTL( )  method is deprecated in Java 1.2 and later because it doesn't properly handle TTLs greater than 127it truncates them to 127. The  getTimeToLive( )  method can handle the full range from 1 to 255 without truncation because it returns an  int  instead of a  byte  .    14.2.2.12 public void setLoopbackMode(boolean disable) throws SocketException // Java 1.4   Whether or not a host receives the multicast packets it sends is platform-dependentthat is, whether or not they loop back. Passing  true  to  setLoopback()  indicates you don't want to receive the packets you send. Passing  false  indicates you do want to receive the packets you send. However, this is only a hint. Implementations are not required to do as you request.    14.2.2.13 public boolean getLoopbackMode( ) throws SocketException // Java 1.4   Because loopback mode is only a hint that may not be followed on all systems, it's important to check what the loopback mode is if you're both sending and receiving packets. The  getLoopbackMode()  method returns  true  if packets are not looped back and  false  if they are. (This feels backwards to me. I suspect this method was written by a programmer following the ill-advised convention that defaults should always be true.)    If the system is looping packets back and you don't want it to, you'll need to recognize the packets somehow and discard them. If the system is not looping the packets back and you do want it to, store copies of the packets you send and inject them into your internal data structures manually at the same time you send them. You can ask for the behavior you want with  setLoopback( )  , but you can't count on it.    |