Working with Multicast Sockets
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, The constructors are simple. Each one calls the equivalent constructor in the 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 This constructor creates a socket that receives datagrams on a well-known port. The Starting in Java 1.4, you can create a However, the Finally, you can pass null to this constructor to create an unbound socket, which would later be connected with the Once a The To receive data from a 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 Java 1.4 adds this overloaded variant of Other than the extra argument specifying the network interface to listen from, this behaves pretty much like the single argument The 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 Sending data with a The data is sent to every host that belongs to the multicast group to which the packet is addressed. For example:
However, the On a multihomed host, the If you need to know the address of the interface the socket is bound to, call The The The The The The Whether or not a host receives the multicast packets it sends is platform-dependent-that is, whether or not they loop back. Passing 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 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 tutorial rather than reading it cover to cover, now might be a good time to go back and read 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.
The Constructors
DatagramSocket
superclass.
public MulticastSocket( ) throws SocketException
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);
}
public MulticastSocket(int port) throws SocketException
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);
}
public MulticastSocket(SocketAddress bindAddress) throws IOException // Java 1.4
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);
}
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);
}
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);
}
Communicating with a Multicast Group
MulticastSocket
has been created, it can perform four key operations:
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.
public void joinGroup(InetAddress address) throws IOException
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);
}
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.
public void joinGroup(SocketAddress address, NetworkInterface interface) throws IOException // Java 1.4
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);
}
joinGroup( )
method. For instance, passing a SocketAddress
object that does not represent a multicast group as the first argument throws an IOException
.
public void leaveGroup(InetAddress address) throws IOException
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.
public void leaveGroup(SocketAddress multicastAddress, NetworkInterface interface) throws IOException // Java 1.4
joinGroup( )
.
public void send(DatagramPacket packet, byte ttl) throws IOException
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
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);
}
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);
public void setInterface(InetAddress address) throws SocketException
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);
}
public InetAddress getInterface( ) throws SocketException
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);
}
public void setNetworkInterface(NetworkInterface interface) throws SocketException // Java 1.4
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.
public NetworkInterface getNetworkInterface( ) throws SocketException // Java 1.4
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( ));
public void setTimeToLive(int ttl) throws IOException // Java 1.2
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
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.
public int getTimeToLive( ) throws IOException // Java 1.2
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
getTTL( )
method is deprecated in Java 1.2 and later because it doesn't properly handle TTLs greater than 127-it 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
.
public void setLoopbackMode(boolean disable) throws SocketException // Java 1.4
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.
public boolean getLoopbackMode( ) throws SocketException // Java 1.4
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.