By and large, the majority of Internet traffic is unicast traffic, consisting of unique conversations between pairs of parties on the network, as shown in Figure 1 below. When you send email to someone, for example, a network connection is established between you and a mail exchanger, some commands are exchanged, and the body of your message is transmitted. When you visit a Web site, a network connection is established between you and the Web server, some commands are exchanged, and your computer receives the body of the page. In these scenarios, unicast makes sense; it is quite probable that your client and the remote server are engaging in a unique conversation on the network at that instant.
Consider, however, the case of a streaming Internet radio station. In this case, the server will simultaneously transmit the same data to many different receivers. In other words, it will send the same packets of data multiple times, once for each connected client. It would potentially be much more efficient if the server could place a single packet onto the network and deliver that packet to all recipients without duplication. This scenario is obviously not practical, though; different clients would be on different networks, and each packet would have to trace a tortuous route to reach everyone along a single path.
Instead, consider a scenario, depicted in Figure 2, in which the server places a single packet onto the network and the network determines an optimal route to all clients, duplicating the packet only when necessary. The packet will thus travel outwards in a tree pattern, duplicating itself at branches in the network path from the server to all clients. This is IP multicast.
This article is not going to show you how to create an Internet radio station; that would be far too complicated. Instead, the particular application that we will look at is a networked chat system. Here again, multicast will serve the goal of efficiency: when the system transmits a message to a chat room, rather than unicast it once to each client, the system can multicast it in a single operation to all clients, and the network will take care of optimal distribution.
Given a network that is capable of multicasting data, there are in fact two ways to design this chat system. One option, shown in Figure 3 below, would employ a central server to which clients unicast their messages and which then multicasts the messages back out. This is perhaps the most obvious redesign of a traditional Web-based chat system; however, it is not necessarily the best.
Multicast is in no way restricted to a traditional server-based architecture. At any given time, any member of the group is equally likely to take the duty of multicasting to all others. This capability lends itself to a much neater architecture for our chat system than the server-based system that I just described.
Consider, instead, a serverless peer-to-peer architecture, illustrated in Figure 4 below. In this architecture, members of the group multicast their messages directly to each other, simulating much more accurately a normal conversation among a group of people. This is the architecture that I will employ for this article. It should, of course, be noted that converting this to a server-based architecture is comparatively trivial, for those who are interested.
I'm not planning on going into great depth here about the internals of multicast; others have done more than I could ever hope to. If you'd like more multicast details, I've included some helpful links in the Resources section at the end of this article. However, I do have to present two big caveats about multicast up front: multicasting is, by itself, neither reliable nor pervasive.
If you multicast some data, it may be lost or delivered out of order; this represents multicasting's main problem with reliability. Also, losses and misordering may vary by recipient, which means that some recipients may receive the data as intended while others may experience various losses. It is possible to layer protocols that add reliability on top of raw multicast; the Java shared data toolkit employs one such protocol, LRMP (Lightweight Reliable Multicast Protocol). However, without the use of such a higher-level protocol, applications have to be able to tolerate data loss.
The second problem, multicasting's lack of pervasiveness, is due to the fact that multicast is not deployed on the Internet at large. There are islands (for example, Ethernet wires, corporations, university campuses, and various ISPs) that support multicast, and there are technologies (the MBone, for example, or multicast-enabled routers) that can link multicast-enabled islands across the Internet. But, by and large, random machines on the Internet cannot communicate using IP multicast. As a result, multicast is currently suitable only for controlled environments.
I'm also going to skimp on a detailed introduction to Java's specific multicast-related classes. For the purposes of this article, however, I've included a brief sidebar on them to get you started.
To keep things manageable, our chat system will be purely text-based: clients will communicate among themselves using simple strings of text. For universality, of course, we will use Java's character streams to support the characters of any (supported) language. Also, as stated earlier, our chat system will be purely peer-to-peer; unlike most traditional client/server chat systems, there will be no server. All clients are created equal in the eyes of this system.
Some questions naturally follow from the use of this architecture. How, using the multicast model, do we define a chat room? And how do interested persons gather there to chat? In a client-server system, you name the server, port, and, if your protocol demands it, a room. In this system, however, there is no server to identify. In the world of multicast, the same idea of a central gathering point is implemented with the concept of a multicast group. Under the current version of IP, there are 268 million multicast groups, identified by the IP addresses 220.127.116.11 through 18.104.22.168. When you join a particular group, you announce to the underlying network infrastructure that you are interested in messages sent to that group. The network will then attempt to ensure that you receive all messages sent to that group across the entire multicast-enabled archipelago to which you are connected. To send a message to a group, simply drop it onto the network, addressed to the appropriate group. The network will then attempt to deliver your message to all members of that group. Indeed, you do not even need to be a member of a group to send messages to it.
So, how do you know which group is yours? Well, that's a big picture issue. You can apply to the Internet Architecture and Naming Authority (IANA), if you want; however, it's doubtful that IANA would assign you a group number just for a chat room. More often, if the group will be restricted to just your organization, you can ask your system administrator to assign one of the 16 million private-use group numbers (those beginning with 239).
To make administration easier, multicast supports a special time-to-live feature whereby you can limit how far messages will travel. You can restrict a chat group to just your department intranet, for instance, or perhaps to your local Ethernet wire. There are two aspects to this. First, you must configure the routers and tunnels of your network appropriately. Usually, this is performed by your system administrator. Second, you must choose an appropriate time-to-live for the messages that you send; the value will depend on your local network configuration. A time-to-live of 1 will restrict packets to your local Ethernet wire, while 63 typically restricts it to a single multicast island. You can find more details in the Resources.
We now have an overall design for the chat system:
Configure your network to be multicast-enabled. If you have multiple machines on a single hub or switch, they should be able to communicate using multicast without any configuration.
Choose a multicast group and a time-to-live that lets all the desired users connect to your chat session, but prevents messages from travelling too far. For testing purposes, any multicast group in the 239.x.x.x range will do, as will a time-to-live of 1.
Join the multicast group so that you receive messages destined for it. Start a thread that listens for these messages and display them in a text area.
- Open a text field for the user to enter messages. To transmit a message to the multicast group, simply drop it on the network bound to the multicast address.
IP multicast is built on top of the User Datagram Protocol (UDP). This type of network protocol is packet-based; you construct a packet of information (up to 65,508 bytes long) and dispatch the packet (a datagram) into the network, bound for a particular destination (identified by an IP address and a port). The network will then try to deliver the packet to the remote address. Upon successful delivery, the recipient will receive the exact payload that you initially dispatched. However, the network sometimes loses the datagrams, sometimes duplicates them, and sometimes misorders them (that is, packets may arrive in a different order than the one in which they were sent). Neither the sender nor the recipient will receive notification of any such network errors. Rather, it is up to the application to identify and surmount such problems. This is one of the complexities of datagram-based networking. The datagram protocols only guarantee that, if a packet is delivered, it will be delivered intact.
For many applications -- file transfer, downloading Web pages, and so on -- datagram protocols are completely inappropriate. These data types must be transferred intact, so they require a reliable protocol (such as TCP) to guarantee successful transfer. In other cases -- streaming audio, for example -- datagram protocols are fine; occasional problems, such as data loss, are easily overcome or ignored. In the case of our chat system, we will simply ignore these problems. For our purposes, the occasional loss of a message is quite acceptable. Protocols (see Resources) that implement reliable transfer on top of multicast do exist. However, these go beyond the scope of the problem that I wish to address here.
Now we come down to the issue of the datagram format. You could support sequence numbers to detect loss and misordering, include information about the sender, and so on. I want to keep things simple, however, so the contents of our datagrams are simply going to be a sequence of UTF 8-encoded characters. If packets could be corrupted, the decoding could fail and cause problems. However, datagram protocols guarantee payload integrity, so this is not an issue here.
The next question concerns the method by which we send the messages. One option would be for the application to simply take a message, convert it to a byte array, create a datagram from this array, and deliver the datagram to the network. To receive messages, the application would receive datagrams from the network, extract the payloads, convert these to strings, and display them.
This is too low-level for my liking. Instead, I'm going to define a pair of simple stream classes, one that translates a stream of data into datagrams that are placed on the network, and another that translates datagrams from the network into a stream of data. Our application can thus use a convenient high-level streams interface to communicate with the multicast network.
Of course, there are advantages and disadvantages to this approach. On the plus side, it is easy to use. It also lets us potentially build in some degree of reliability underneath our application without changing the public API. On the minus side, however, it does hide the datagram nature of the underlying protocol. We are discarding some information in the process as well: the origins of a message, what the boundary of a particular message is, and so on. When you notice that there is nothing to stop a random machine multicast-connected to our chat system from sending any data to our multicast group, the potential problems become clear. However, regardless of the advantages and disadvantages, this approach serves my whim.
Let's get down to the nitty-gritty. This entire chat system consists of three classes:
DatagramOutputStream: An output byte stream that, whenever its
flush()method is invoked, collects data into a byte array for transmission as a datagram packet.
DatagramInputStream: An input byte stream that receives packets from the network and translates them into a continuous stream of data.
MulticastChat: The main chat application. It consists of a
Framecontaining an output
TextAreaand an input
TextField. Messages entered in the
TextFieldtransmit to the multicast group. A listener thread receives messages from the multicast group and displays them in the
We start with our