Java多播通信编程

Published on 2017 - 05 - 07

Multicasting

Multicasting is broader than unicast, point-to-point communication but narrower and more targeted than broadcast communication. Multicasting sends data from one host to many different hosts, but not to everyone; the data only goes to clients that have expressed an interest by joining a particular multicast group. In a way, this is like a public meeting. People can come and go as they please, leaving when the discussion no longer interests them. Before they arrive and after they have left, they don’t need to process the information at all: it just doesn’t reach them. On the Internet, such “public meetings” are best implemented using a multicast socket that sends a copy of the data to a location (or a group of locations) close to the parties that have declared an interest in the data. In the best case, the data is duplicated only when it reaches the local network serving the interested clients: the data crosses the Internet only once. More realistically, several identical copies of the data traverse the Internet; but, by carefully choosing the points at which the streams are duplicated, the load on the network is minimized. The good news is that programmers and network administrators aren’t responsible for choosing the points where the data is duplicated or even for sending multiple copies; the Internet’s routers handle all that.

IP also supports broadcasting, but the use of broadcasts is strictly limited. Protocols require broadcasts only when there is no alternative; and routers limit broadcasts to the local network or subnet, preventing broadcasts from reaching the Internet at large. Even a few small global broadcasts could bring the Internet to its knees. Broadcasting high-bandwidth data such as audio, video, or even text and still images is out of the question. A single email spam that goes to millions of addresses is bad enough. Imagine what would happen if a real-time video feed were copied to all billion+ Internet users, whether they wanted to watch it or not.

However, there’s a middle ground between point-to-point communications and broadcasts to the whole world. There’s no reason to send a video feed to hosts that aren’t interested in it; we need a technology that sends data to the hosts that want it, without bothering the rest of the world. One way to do this is to use many unicast streams. If 1,000 clients want to watch a BBC live stream, the data is sent a thousand times. This is inefficient, since it duplicates data needlessly, but it’s orders-of-magnitude more efficient than broadcasting the data to every host on the Internet. Still, if the number of interested clients is large enough, you will eventually run out of bandwidth or CPU power—probably sooner rather than later.

Another approach to the problem is to create static connection trees. This is the solution employed by Usenet news and some conferencing systems. Data is fed from the originating site to other servers, which replicate it to still other servers, which eventually replicate it to clients. Each client connects to the nearest server. This is more efficient than sending everything to all interested clients via multiple unicasts, but the scheme is kludgy and beginning to show its age. New sites need to find a place to hook into the tree manually. The tree does not necessarily reflect the best possible topology at any one time, and servers still need to maintain many point-to-point connections to their clients, sending the same data to each one. It would be better to allow the routers in the Internet to dynamically determine the best possible routes for transmitting distributed information and to replicate data only when absolutely necessary. This is where multicasting comes in.

For example, if you’re multicasting video from New York and 20 people attached to one LAN are watching the show in Los Angeles, the feed will be sent to that LAN only once. If 50 more people are watching in San Francisco, the data stream will be duplicated somewhere (let’s say Fresno) and sent to the two cities. If a hundred more people are watching in Houston, another data stream will be sent there (perhaps from St. Louis); see Figure 1. The data has crossed the Internet only three times—not the 170 times that would be required by point-to-point connections, or the millions of times that would be required by a true broadcast. Multicasting is halfway between the point-to-point communication common to the Internet and the broadcast model of television and it’s more efficient than either. When a packet is multicast, it is addressed to a multicast group and sent to each host belonging to the group. It does not go to a single host (as in unicasting), nor does it go to every host (as in broadcasting). Either would be too inefficient.

When people talk about multicasting, audio and video are the first applications that come to mind. Indeed, the BBC has been running a multicast trial covering both TV and radio for several years now, though ISP participation has been regrettably limited. However, audio and video are only the tip of the iceberg. Other possibilities include multiplayer games, distributed filesystems, massively parallel computing, multiperson conferencing, database replication, content delivery networks, and more. Multicasting can be used to implement name services and directory services that don’t require the client to know a server’s address in advance; to look up a name, a host could multicast its request to some well-known address and wait until a response is received from the nearest server. Apple’s Bonjour (a.k.a. Zeroconf) and Apache’s River both use IP multicasting to dynamically discover services on the local network.

Multicasting has been designed to fit into the Internet as seamlessly as possible. Most of the work is done by routers and should be transparent to application programmers. An application simply sends datagram packets to a multicast address, which isn’t fundamentally different from any other IP address. The routers make sure the packet is delivered to all the hosts in the multicast group. The biggest problem is that multicast routers are not yet ubiquitous; therefore, you need to know enough about them to find out whether multicasting is supported on your network. For instance, although the BBC has been multicasting for several years now, their multicast streams are only accessible to subscribers of about a dozen relatively small British ISPs. In practice, multicasting is much more commonly used behind the firewall within a single organization than across the global Internet.

As far as the application itself, you need to pay attention to an additional header field in the datagrams called the Time-To-Live (TTL) value. The TTL is the maximum number of routers that the datagram is allowed to cross. Once the packet has crossed that many routers, it is discarded. Multicasting uses the TTL as an ad hoc way to limit how far a packet can travel. For example, you don’t want packets for a friendly on-campus game of Dogfight reaching routers on the other side of the world. Figure 2 shows how TTLs limit a packet’s spread.

Multicast Addresses and Groups

A multicast address is the shared address of a group of hosts called a multicast group. We’ll talk about the address first. IPv4 multicast addresses are IP addresses in the CIDR group 224.0.0.0/4 (i.e., they range from 224.0.0.0 to 239.255.255.255). All addresses in this range have the binary digits 1110 as their first four bits. IPv6 multicast addresses are in the CIDR group ff00::/8 (i.e., they all start with the byte 0xFF, or 11111111 in binary).

Like any IP address, a multicast address can have a hostname. For example, the multicast address 224.0.1.1 (the address of the Network Time Protocol distributed service) is assigned the name ntp.mcast.net.

A multicast group is a set of Internet hosts that share a multicast address. Any data sent to the multicast address is relayed to all the members of the group. Membership in a multicast group is open; hosts can enter or leave the group at any time. Groups can be either permanent or transient. Permanent groups have assigned addresses that remain constant, whether or not there are any members in the group. However, most multicast groups are transient and exist only as long as they have members. All you have to do to create a new multicast group is pick a random address from 225.0.0.0 to 238.255.255.255, construct an InetAddress object for that address, and start sending it data.

The IANA is responsible for handing out permanent multicast addresses as needed. So far, a few hundred have been specifically assigned. Link-local multicast addresses begin with 224.0.0 (i.e., addresses from 224.0.0.0 to 224.0.0.255) and are reserved for routing protocols and other low-level activities, such as gateway discovery and group membership reporting. For example, all-systems.mcast.net, 224.0.0.1, is a multicast group that includes all systems on the local subnet. Multicast routers never forward datagrams with destinations in this range. Table 1 lists a few of these assigned addresses.

Domain name IP address Purpose
BASE-ADDRESS.MCAST.NET 224.0.0.0 The reserved base address. This is never assigned to any multicast group.
ALL-SYSTEMS.MCAST.NET 224.0.0.1 All systems on the local subnet.
ALL-ROUTERS.MCAST.NET 224.0.0.2 All routers on the local subnet.
DVMRP.MCAST.NET 224.0.0.4 All Distance Vector Multicast Routing Protocol (DVMRP) routers on this subnet.
MOBILE-AGENTS.MCAST.NET 224.0.0.11 Mobile agents on the local subnet.
DHCP-AGENTS.MCAST.NET 224.0.0.12 This multicast group allows a client to locate a Dynamic Host Configuration Protocol (DHCP) server or relay agent on the local subnet.
RSVP-ENCAPSULATION.MCAST.NET 224.0.0.14 RSVP encapsulation on this subnet. RSVP stands for Resource reSerVation setup Protocol, an effort to allow people to reserve a guaranteed amount of Internet bandwidth in advance for an event.
VRRP.MCAST.NET 224.0.0.18 Virtual Router Redundancy Protocol (VRRP) Routers
224.0.0.35 DXCluster is used to announce foreign amateur (DX) stations.
224.0.0.36 Digital Transmission Content Protection (DTCP), a digital restrictions management (DRM) technology that encrypts interconnections between DVD players, televisions, and similar devices.
224.0.0.37-224.0.0.68 zeroconf addressing
224.0.0.106 Multicast Router Discovery
224.0.0.112 Multipath Management Agent Device Discovery
224.0.0.113 Qualcomm’s AllJoyn
224.0.0.114 Inter RFID Reader Protocol
224.0.0.251 Multicast DNS self assigns and resolves host names for multicast addresses.
224.0.0.252 Link-local Multicast Name Resolution, a precursor of mDNS, allows nodes ot self-assign domain names strictly for the local network, and to resolve such domain names on the local network.
224.0.0.253 Teredo is used to tunnel IPv6 over IPv4. Other Teredo clients on the same IPv4 subnet respond to this multicast address.
224.0.0.254 Reserved for experimentation.

Permanently assigned multicast addresses that extend beyond the local subnet begin with 224.1. or 224.2. Table 2 lists a few of these permanent addresses. A few blocks of addresses ranging in size from a few dozen to a few thousand addresses have also been reserved for particular purposes. The complete list is available from iana.org, though you should note that it contains many now defunct services, protocols, and companies. The remaining 248 million multicast addresses can be used on a temporary basis by anyone who needs them. Multicast routers (mrouters for short) are responsible for making sure that two different systems don’t try to use the same address at the same time.

Domain name IP address Purpose
NTP.MCAST.NET 224.0.1.1
NSS.MCAST.NET 224.0.1.6
AUDIONEWS.MCAST.NET 224.0.1.7
MTP.MCAST.NET 224.0.1.9
IETF-1-LOW-AUDIO.MCAST.NET 224.0.1.10
IETF-1-AUDIO.MCAST.NET 224.0.1.11
IETF-1-VIDEO.MCAST.NET 224.0.1.12
IETF-2-LOW-AUDIO.MCAST.NET 224.0.1.13
IETF-2-AUDIO.MCAST.NET 224.0.1.14
IETF-2-VIDEO.MCAST.NET 224.0.1.15
MLOADD.MCAST.NET 224.0.1.19
EXPERIMENT.MCAST.NET 224.0.1.20
224.0.23.178
MICROSOFT.MCAST.NET 224.0.1.24
MTRACE.MCAST.NET 224.0.1.32
JINI-ANNOUNCEMENT.MCAST.NET 224.0.1.84
JINI-REQUEST.MCAST.NET 224.0.1.85
224.0.1.143
224.2.0.0-224.2.255.255
224.2.2.2
239.0.0.0-239.255.255.255

Clients and Servers

When a host wants to send data to a multicast group, it puts that data in multicast datagrams, which are nothing more than UDP datagrams addressed to a multicast group. Multicast data is sent via UDP, which, though unreliable, can be as much as three times faster than data sent via connection-oriented TCP. (If you think about it, multicast over TCP would be next to impossible. TCP requires hosts to acknowledge that they have received packets; handling acknowledgments in a multicast situation would be a nightmare.) If you’re developing a multicast application that can’t tolerate data loss, it’s your responsibility to determine whether data was damaged in transit and how to handle missing data. For example, if you are building a distributed cache system, you might simply decide to leave any files that don’t arrive intact out of the cache.

Earlier, I said that from an application programmer’s standpoint, the primary difference between multicasting and using regular UDP sockets is that you have to worry about the TTL value. This is a single byte in the IP header that takes values from 1 to 255; it is interpreted roughly as the number of routers through which a packet can pass before it is discarded. Each time the packet passes through a router, its TTL field is decremented by at least one; some routers may decrement the TTL by two or more. When the TTL reaches zero, the packet is discarded. The TTL field was originally designed to prevent routing loops by guaranteeing that all packets would eventually be discarded. It prevents misconfigured routers from sending packets back and forth to each other indefinitely. In IP multicasting, the TTL limits the multicast geographically. For example, a TTL value of 16 limits the packet to the local area, generally one organization or perhaps an organization and its immediate upstream and downstream neighbors. A TTL of 127, however, sends the packet around the world. Intermediate values are also possible. However, there is no precise way to map TTLs to geographical distance. Generally, the farther away a site is, the more routers a packet has to pass through before reaching it. Packets with small TTL values won’t travel as far as packets with large TTL values. Table 3 provides some rough estimates relating TTL values to geographical reach. Packets addressed to a multicast group from 224.0.0.0 to 224.0.0.255 are never forwarded beyond the local subnet, regardless of the TTL values used.

Destinations TTL value
The local host 0
The local subnet 1
The local campus—that is, the same side of the nearest Internet router—but on possibly different LANs 16
High-bandwidth sites in the same country, generally those fairly close to the backbone 32
All sites in the same country 48
All sites on the same continent 64
High-bandwidth sites worldwide 128
All sites worldwide 255

Once the data has been stuffed into one or more datagrams, the sending host launches the datagrams onto the Internet. This is just like sending regular (unicast) UDP data. The sending host begins by transmitting a multicast datagram to the local network. This packet immediately reaches all members of the multicast group in the same subnet. If the Time-To-Live field of the packet is greater than 1, multicast routers on the local network forward the packet to other networks that have members of the destination group. When the packet arrives at one of the final destinations, the multicast router on the foreign network transmits the packet to each host it serves that is a member of the multicast group. If necessary, the multicast router also retransmits the packet to the next routers in the paths between the current router and all its eventual destinations.

When data arrives at a host in a multicast group, the host receives it as it receives any other UDP datagram—even though the packet’s destination address doesn’t match the receiving host. The host recognizes that the datagram is intended for it because it belongs to the multicast group to which the datagram is addressed, much as most of us accept mail addressed to “Occupant,” even though none of us are named Mr. or Ms. Occupant. The receiving host must be listening on the proper port, ready to process the datagram when it arrives.

Routers and Routing

Figure 3 shows one of the simplest possible multicast configurations: a single server sending the same data to four clients served by the same router. A multicast socket sends one stream of data over the Internet to the clients’ router; the router duplicates the stream and sends it to each of the clients. Without multicast sockets, the server would have to send four separate but identical streams of data to the router, which would route each stream to a client. Using the same stream to send the same data to multiple clients significantly reduces the bandwidth required on the Internet backbone.

Of course, real-world routes can be much more complex, involving multiple hierarchies of redundant routers. However, the goal of multicast sockets is simple: no matter how complex the network, the same data should never be sent more than once over any given network segment. Fortunately, you don’t need to worry about routing issues. Just create a MulticastSocket, have the socket join a multicast group, and stuff the address of the multicast group in the DatagramPacket you want to send. The routers and the MulticastSocket class take care of the rest.

The biggest restriction on multicasting is the availability of special multicast routers (mrouters). Mrouters are reconfigured Internet routers or workstations that support the IP multicast extensions. Many consumer-oriented ISPs quite deliberately do not enable multicasting in their routers. In 2013, it is still possible to find hosts between which no multicast route exists (i.e., there is no route between the hosts that travels exclusively over mrouters).

To send and receive multicast data beyond the local subnet, you need a multicast router. Check with your network administrator to see whether your routers support multicasting. You can also try pinging all-routers.mcast.net . If any router responds, then your network is hooked up to a multicast router:

% ping all-routers.mcast.net
all-routers.mcast.net is alive

This still may not allow you to send to or receive from every multicast-capable host on the Internet. For your packets to reach any given host, there must be a path of multicast-capable routers between your host and the remote host. Alternatively, some sites may be connected by special multicast tunnel software that transmits multicast data over unicast UDP that all routers understand. If you have trouble getting the examples in this chapter to produce the expected results, check with your local network administrator or ISP to see whether multicasting is actually supported by your routers.

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
    implements Closeable, AutoCloseable

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.

To receive data that is being multicast from a remote site, first create a MulticastSocket with the MulticastSocket() constructor. As with other kinds of sockets, you need to know the port to listen on. This code fragment opens a MulticastSocket that listens on port 2300:

MulticastSocket ms = new MulticastSocket(2300);
Next, join a multicast group using the MulticastSockets joinGroup() method:
InetAddress group = InetAddress.getByName("224.2.2.2");
ms.joinGroup(group);

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. 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:

byte[] buffer = new byte[8192];
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ms.receive(dp);

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:

ms.leaveGroup(group);
ms.close();

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:

InetAddress ia = InetAddress.getByName("experiment.mcast.net");
byte[] data = "Here's some multicast data\r\n".getBytes("UTF-8");
int port = 4000;
DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);
MulticastSocket ms = new MulticastSocket();
ms.send(dp);

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 only 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

The constructors are simple. You can either pick a port to listen on or let Java assign an anonymous port for you:

public MulticastSocket() throws SocketException
public MulticastSocket(int port) throws SocketException
public MulticastSocket(SocketAddress bindAddress) throws IOException

For example:

MulticastSocket ms1 = new MulticastSocket();
MulticastSocket ms2 = new MulticastSocket(4000);
SocketAddress address = new InetSocketAddress("192.168.254.32", 4000);
MulticastSocket ms3 = new MulticastSocket(address);

All three constructors throw a SocketException if the 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, then a Socket cannot be created. Note that because 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.

You can pass null to the constructor to create an unbound socket, which will be connected later 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):

MulticastSocket ms = new MulticastSocket(null);
ms.setReuseAddress(false);
SocketAddress address = new InetSocketAddress(4000);
ms.bind(address);

Communicating with a Multicast Group

Once a MulticastSocket has been created, it can perform four key operations:

  1. Join a multicast group.
  2. Send data to the members of the group.
  3. Receive data from the group.
  4. Leave the multicast group.

The MulticastSocket class has methods for operations 1 and 4. No new methods are required to send or receive data. The send() and receive() methods of the superclass, DatagramSocket, suffice for those operations. You can perform these operations in any order, with the exception that you must join a group before you can receive data from it. You do not need to join a group to send data to it, and you can freely intermix sending and receiving data.

Joining groups

To join a group, pass an InetAddress or a SocketAddress for the multicast group to the joinGroup() method:

public void joinGroup(InetAddress address) throws IOException
public void joinGroup(SocketAddress address, NetworkInterface interface)
    throws IOException

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 (if 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, each socket receives a complete copy of the data addressed to that group that arrives at the local host.

A second argument allows you to join a multicast group only on a specified local network interface. 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();
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.

Leaving groups and closing the connection

Call the leaveGroup() method when you no longer want to receive datagrams from the specified multicast group, on either all or a specified network interface:

public void leaveGroup(InetAddress address) throws IOException
public void leaveGroup(SocketAddress multicastAddress,
NetworkInterface interface)
    throws IOException

It signals the local multicast router, telling it to stop sending you datagrams. If the address you try to leave is not a multicast address (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.

Pretty much all the methods in MulticastSocket can throw an IOException, so you’ll usually wrap all this in a try block. In Java 7, DatagramSocket implements Autocloseable so you can use try-with-resources:

try (MulticastSocket socket = new MulticastSocket()) {
  // connect to the server...
} catch (IOException ex) {
  ex.printStackTrace();
}

In Java 6 and earlier, you’ll want to explicitly close the socket in a finally block to release resources the socket holds:

MulticastSocket socket = null;
try {
  socket = new MulticastSocket();
  // connect to the server...
} catch (IOException ex) {
  ex.printStackTrace();
} finally {
  if (socket != null) {
    try {
      socket.close();
    } catch (IOException ex) {
      // ignore
    }
  }
}

Sending multicast data

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. 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);
}

By default, multicast sockets 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 first argument to the constructor.

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). The getTimeToLive() method returns the default TTL value of the MulticastSocket:

public void setTimeToLive(int ttl) throws IOException
public int getTimeToLive() throws IOException

For example, this code fragment sets a TTL of 64:

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.setTimeToLive(64);
  ms.send(dp);
} catch (IOException ex) {
  System.err.println(ex);
}

Loopback mode

Whether or not a host receives the multicast packets it sends is platform dependent—that 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:

public void setLoopbackMode(boolean disable) throws SocketException
public boolean getLoopbackMode() throws SocketException

However, this is only a hint. Implementations are not required to do as you request. Because loopback mode 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 backward 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.

Network interfaces

On a multihomed host, the setInterface() and setNetworkInterface() methods choose the network interface used for multicast sending and receiving:

public void setInterface(InetAddress address) throws SocketException
public InetAddress getInterface() throws SocketException
public void setNetworkInterface(NetworkInterface interface) throws
            SocketException
public NetworkInterface getNetworkInterface() throws SocketException

The setter methods throw a SocketException if the 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():

try {
  InetAddress ia = InetAddress.getByName("www.ibiblio.org");
  MulticastSocket 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);
}

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.

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());

Two Simple Examples

Most multicast servers are indiscriminate about who they will talk to. Therefore, it’s easy to join a group and watch the data that’s being sent to it. Example 1 is a MulticastSniffer class that reads the name of a multicast group from the command line, constructs an InetAddress from that hostname, and creates a MulticastSocket, which attempts to join the multicast group at that hostname. If the attempt succeeds, MulticastSniffer receives datagrams from the socket and prints their contents on System.out. This program is useful primarily to verify that you are receiving multicast data at a particular host. Most multicast data is binary and won’t be intelligible when printed as text.

import java.io.*;
import java.net.*;

public class MulticastSniffer {

  public static void main(String[] args) {

    InetAddress group = null;
    int port = 0;

    // read the address from the command line
    try {
      group = InetAddress.getByName(args[0]);
      port = Integer.parseInt(args[1]);
    } catch (ArrayIndexOutOfBoundsException | NumberFormatException
        | UnknownHostException ex) {
      System.err.println(
          "Usage: java MulticastSniffer multicast_address port");
      System.exit(1);
    }

    MulticastSocket ms = null;
    try {
      ms = new MulticastSocket(port);
      ms.joinGroup(group);

      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);
    } finally {
      if (ms != null) {
        try {
          ms.leaveGroup(group);
          ms.close();
        } catch (IOException ex) {}
      }
    }
  }
}

The program begins by reading the name and port of the multicast group from the first two command-line arguments. Next, it creates a new MulticastSocket ms on the specified port. This socket joins the multicast group at the specified InetAddress. Then it enters a loop in which it waits for packets to arrive. As each packet arrives, the program reads its data, converts the data to an ISO Latin-1 String, and prints it on System.out. Finally, when the user interrupts the program or an exception is thrown, the socket leaves the group and closes itself.

When a Universal Plug and Play (UPnP) device joins a network, it sends an HTTPU (HTTP over UDP) message to the multicast address 239.255.255.250 on port 1900. You can use this program to listen to those messages. If such a device is broadcasting, you should see a message pop through within the first minute or two. In fact, you’ll probably see a lot more. I collected about a megabyte and a half of announcements within the first couple of minutes I had this program running. I show only the first two here:

$ java MulticastSniffer 239.255.255.250 1900
NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=1800
LOCATION: http://192.168.1.2:23519/Ircc.xml
NT: upnp:rootdevice
NTS: ssdp:alive
SERVER: Android/3.2 UPnP/1.0 Internet TV Box NSZ-GT1/1.0
USN: uuid:34567890-1234-1010-8000-544249cb49fd::upnp:rootdevice
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation";
mn="Internet TV Box NSZ-GT1"; mv="1.0";

NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=1800
LOCATION: http://192.168.1.2:23519/Ircc.xml
NT: uuid:34567890-1234-1010-8000-544249cb49fd
NTS: ssdp:alive
SERVER: Android/3.2 UPnP/1.0 Internet TV Box NSZ-GT1/1.0
USN: uuid:34567890-1234-1010-8000-544249cb49fd
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation";
mn="Internet TV Box NSZ-GT1"; mv="1.0";

It appears that my Google TV is very chatty, sending an announcement about once a second. Most devices only announce when they’re first connected to the network, or when they’re queried by another device.

Now let’s consider sending multicast data. Example 2 is a MulticastSender class that reads input from the command line and sends it to a multicast group. It’s fairly simple, overall.

import java.io.*;
import java.net.*;

public class MulticastSender {

  public static void main(String[] args) {

    InetAddress ia = null;
    int port = 0;
    byte ttl = (byte) 1;

    // read the address from the command line
    try {
      ia = InetAddress.getByName(args[0]);
      port = Integer.parseInt(args[1]);
      if (args.length > 2) ttl = (byte) Integer.parseInt(args[2]);
    } catch (NumberFormatException | IndexOutOfBoundsException
        | UnknownHostException ex)  {
      System.err.println(ex);
      System.err.println(
          "Usage: java MulticastSender multicast_address port ttl");
      System.exit(1);
    }

    byte[] data = "Here's some multicast data\r\n".getBytes();
    DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);

    try (MulticastSocket ms = new MulticastSocket()) {
      ms.setTimeToLive(ttl);
      ms.joinGroup(ia);
      for (int i = 1; i < 10; i++) {
        ms.send(dp);
      }
      ms.leaveGroup(ia);
    } catch (SocketException ex) {
      System.err.println(ex);
    } catch (IOException ex) {
      System.err.println(ex);
    }
  }
}

Example 2 reads the address of a multicast group, a port number, and an optional TTL from the command line. It then stuffs the string "Here's some multicast data\r\n" into the byte array data using the getBytes() method of java.lang.String, and places this array in the DatagramPacket dp. Next, it constructs the MulticastSocket ms, which joins the group ia. Once it has joined the group, ms sends the datagram packet dp to the group ia 10 times. The TTL value is set to one to make sure that this data doesn’t go beyond the local subnet. Having sent the data, ms leaves the group and closes itself.

Run MulticastSniffer on one machine in your local subnet. Listen to the group all-systems.mcast.net on port 4000, like this:

% java MulticastSniffer all-systems.mcast.net 4000

Next, send data to that group by running MulticastSender on another machine in your local subnet. You can also run it in a different window on the same machine, although that option is not as exciting. However, you must start running the MulticastSniffer before you start running the MulticastSender. Send to the group all-systems.mcast.net on port 4000, like this:

% java MulticastSender all-systems.mcast.net 4000

Back on the first machine, you should see this output:

Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data
Here's some multicast data

For this to work beyond the local subnet, the two subnets must each have multicast routers, and the routers in between them need to have multicast enabled.

Reference