Enough
theory. In Java, you multicast data using the
java.net.MulticastSocket
class. This is 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. However, 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 the previous chapter on UDP.
To receive data that is being multicast from a remote site, you first
create a MulticastSocket
with the
MulticastSocket( )
constructor. Next, you 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, you 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 applets are not allowed to do anything involving multicast sockets. An untrusted applet is allowed to send datagrams to or receive datagrams from the applet host. 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 do and do not receive that data. Consequently, most applet environments take the conservative approach of disallowing all multicasting.
The constructors are simple. Each one
calls the equivalent constructor in the
DatagramSocket
superclass.
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, you can find out 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); }
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 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 that the port
you’re trying to connect to is already occupied. Note that
since, as far as the operating system is concerned, a multicast
socket is a datagram socket, 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 se) { System.err.println(se); }
Once a MulticastSocket
has been created, it can perform four key operations. These are:
Join a multicast group.
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
, is all that’s
needed for receiving. 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.
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 ie) { System.err.println(ie); }
If the address that you try to join is not a multicast address (that
is, it is not from 224.0.0.0 to 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.
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, it is not from 224.0.0.0 to
239.255.255.255), the method throws an
IOException
. However, no exception occurs if you
leave a multicast group you never joined.
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 your packet is addressed. For example:
try { InetAddress ia = InetAddress.getByName("experiment.mcast.net"); byte[] data = "Here's some multicast data ".getBytes( ); int port = 4000; DatagramPacket dp = new DatagramPacket(data, data.length, ia, port); MulticastSocket ms = new MulticastSocket( ); ms.send(dp); } catch (IOException ie) { System.err.println(ie); }
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 for an individual
packet by passing an integer from
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);
On a multihomed host, the setInterface( )
method
chooses the network interface used for multicast sending and
receiving. setInterface( )
throws a
SocketException
if the
InetAddress
you give it 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, you should
set the interface immediately after constructing a
MulticastSocket
and not change it thereafter.
Here’s how you might use setInterface( )
:
MulticastSocket ms; InetAddress ia; try { ia = new InetAddress("metalab.unc.edu"); 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); }
If you need to know the address of the interface you’re using,
you can 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); }
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 available only 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 allows you only to set TTL values from 1 to 127
rather than the full range from 1 to 255.
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 127.
It truncates these 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
.
3.142.12.240