Multicast the chatwaves

Use IP multicast along with custom stream classes to implement a cool, yet simple, peer-to-peer networked chat system

1 2 3 Page 3
Page 3 of 3

In practice

OK, so we now have a streams interface on top of multicast datagrams, and a peer-to-peer multicast chat system built on these streams. How do we try it all out?

The simplest option is to run the application twice on your current machine. Your IP stack should successfully cater to multiple applications listening on the same multicast group and port at the same time. Every message that you enter in the entry field should be displayed by both clients. To get more adventurous, try multiple machines on the same Ethernet bus, hub, or multicast-aware switch. If you have a multicast-capable routed network, you should be able to communicate across networks if you increase the time-to-live. However, you should probably check with your system administrator first.

Running the chat system as an applet is more complex, as Figure 5 below demonstrates. First, there is the question of whether or not the machines running the applet are multicast-connected. Second, there is the issue of whether or not the browser JVMs support multicast; the Java plugin, of course, does. Third, there is the problem of security restrictions. The Java security model does not let an applet receive messages from, or transmit messages to, arbitrary members of a multicast group. It is, in fact, easy (without using applet signing) to overcome this problem; applets are allowed to unicast datagram packets to their codebase server, and to receive multicast messages from that server. As a result, you can adapt the chat system to include a central server to which clients unicast their messages, and which then multicasts the messages back to all clients. But that's an exercise for you, the reader.

Figure 5. An applet-based multicast chat system

Conclusion

Multicast is very cool. The underlying network technology that can determine the best (for some definition of best) way to transfer a single packet to multiple recipients is fascinating, and the potential applications of multicast are quite impressive. Here, we've shown a simple serverless chat system. The simplicity and aesthetic of pure peer-to-peer systems of this nature is very alluring. Furthermore, server-based multicast systems operating efficiently across existing networks are potentially quite powerful. If you follow some of the links in the Resources section below, you'll find some remarkable applications of multicast.

And, to top it all off, Java's elegant I/O architecture lets you hide some of multicast's rough edges -- at a price -- and produce a clean, streams-based interface to the multicast network. All in all, if you compare this chat system with the TCP-based one that I described a few years ago (see the Resources section), I think you'll agree that multicast is infinitely more pleasing. Of course, multicast's nonpervasiveness is a problem; it renders it mildly impractical on the Internet. However, with the imminence of IPv6 (Internet Protocol version 6) and its potential for a wide scale roll-out of multicast, I hope we'll see more applications of multicast in the future.

SIDEBAR:

SIDEBAR_HEAD: Java's multicast-related classes

Because multicast is an extension of UDP (the User Datagram Protocol), Java's multicast classes are closely associated with its unicast datagram classes. The following are the four major related classes:

  • InetAddress: This class represents an IP address; it can be used to resolve a host name into an IP address, and vice versa. This is used to address hosts on a network.

  • DatagramPacket: This class represents an actual datagram; for example, a packet of data that is to be transmitted onto the network, or that has been received from the network.

  • DatagramSocket: This class represents a UDP socket. It lets you place unicast datagrams onto the network for delivery to a remote host, and to receive unicast datagrams from the network.

  • MulticastSocket: This class, a subclass of DatagramSocket, provides specialized support for multicast, specifically the ability to join and leave multicast groups and to set the time-to-live of transmitted datagrams.

The following is a brief listing of the major methods and constructors of these classes. Note that I have, for brevity's sake, omitted the majority of methods and constructors. Only those of immediate concern to this article are included.

Class InetAddress

The InetAddress class is used to represent the address of a host on the network. The following methods are of most common use:

  • static InetAddress getByName(String host): This static method looks up the IP address of a host by name, or constructs an InetAddress object for a specific IP address.

  • boolean isMulticastAddress(): The value of the boolean value returned by this method depends on whether or not an InetAddress object corresponds to a multicast address.

  • String getHostName(): This method returns the host name associated with this InetAddress object; this may involve asking the domain name system to reverse search for the IP address.

  • byte[] getAddress(): This method returns the raw IP address of this InetAddress as an array of bytes.

  • String getHostAddress(): This method returns the raw IP address of this InetAddress in human-readable textual format.

Ports

Note that, when you address a server on the network, you identify not only the IP address (under IPv4, a 4-byte value; under IPv6, a 16-byte value) but also the port (a 16-bit value). A port is, in effect, a subaddress of a particular host. There are 65,535 TCP/IP ports (and, similarly, 65,535 UDP/IP ports) on every machine; however, the UDP ports are shared with IP multicast. And, to further complicate things, ports 1 through 1,024 are reserved for system services.

Thus, when you are running a server (or any other application that accepts incoming network connections), you must choose a port on which to listen. If you are a user application, this port must be above 1,024. Then, when you run the client, you must identify the server IP address (in the case of a client/server system) or the multicast IP address (in the case of a multicast system) and the port on which the server is listening.

Class DatagramPacket

The DatagramPacket class represents a packet of data that is to be transmitted onto or has been received from the network. The following methods are of most common use:

  • DatagramPacket(byte[] data, int length, InetAddress address, int port): This method creates a new DatagramPacket for transmission to the network, consisting of the specified payload, addressed to the specified host and port.

  • DatagramPacket(byte[] data, int length): This method creates a new DatagramPacket for receiving a packet from the network. The packet will be read into the specified buffer. At most, length bytes will be read in, and any additional data discarded.

  • InetAddress getAddress(): For a transmitted packet, this method returns the destination host; for a received packet, this returns the source host.

  • int getPort(): For a transmitted packet, this method returns the destination port; for a received packet, this returns the source port.

  • byte[] getData(): This method returns the data array specified in the constructor.

  • int getLength(): This method returns the length specified in the constructor, or, if a packet has been received from the network, the amount of data read from that packet.

  • void setAddress(InetAddress address): This method reassigns the address of this DatagramPacket.

  • void setPort(Inetint port): This method reassigns the port of this DatagramPacket.

  • void setData(byte[] data): This method reassigns the data array of this DatagramPacket.

  • void setLength(int length): This method reassigns the length of this DatagramPacket.

Observe that a DatagramPacket is simply a container for four fields: address, port, data, and length. In fact, Java 2 has also introduced a fifth offset field to allow more flexibility in access to the byte array. As such, you can easily reuse DatagramPacket for multiple operations; this can be important for efficiency in high-performance servers. However, you must be aware of the effect of previous operations on the internal state of the object. When you receive a packet from the network, use the length field to limit the amount of data that will be read from the received packet. The length field is then updated to reflect the amount of data that has actually been read from the received packet. If you try to receive multiple packets in sequence from the network into a single DatagramPacket, you will find that each subsequent packet is limited in length to the size of the previous packet. To overcome this, you must reset the fields of your DatagramPacket as appropriate between uses.

Class DatagramSocket

The DatagramSocket class transmits and receives datagram (UDP) packets onto and from the network. Note that there is no Java support for raw IP datagrams.

  • DatagramSocket(): This method, primarily used by client applications, creates a new DatagramSocket bound to a random, unused local port.

  • DatagramSocket(int port): This method creates a new DatagramSocket bound to the specified local port port.

  • void send(DatagramPacket packet): This method transmits the specified packet onto the network. When it transmits the packet, the UDP protocol includes the IP address and port of the socket through which it was transmitted. However, the DatagramPacket is not updated with this information. There is no guarantee that you will receive an error if there is no application listening on the destination host and port. In practice, however, in an environment without firewalls, you may receive an error after you transmit a few packets. This error propagation is handled by low-level ICMP (Internet Control Message Protocol) packets.

  • void receive(DatagramPacket packet): This method receives a packet from the network. The method blocks (sleeps) until a packet arrives from the network at this socket. The DatagramPacket is filled with the contents of the received packet, updated with its length, source address, and port.

  • void close(): This method closes this socket.

Java 2 includes a special connected feature that you can use for efficiency if you will be communicating a lot of data with a single remote host in a security-restricted environment. However, it is not of particular interest for this application, since we will communicate with multiple hosts.

Class MulticastSocket

The MulticastSocket class, a subclass of DatagramSocket, provides specialized support for multicast.

  • MulticastSocket(int port): This method creates a new MulticastSocket bound to the specified local port, port.

  • void joinGroup(InetAddress group): This method announces to the underlying network layers that you are interested in receiving packets addressed to the specified multicast group, group. The IGMP (Internet Group Management Protocol) will be used to notify routers as necessary. Subsequently, packets addressed to the multicast group should become available for reception through the receive() method. A single socket can be a member of many multicast groups, although there are often OS-specific restrictions.

  • void leaveGroup(InetAddress group): This method announces to the underlying network layers that you are no longer interested in being a member of the specified group.

  • void setTimeToLive(int ttl): This method sets the default time-to-live of packets transmitted through this socket to the specified value (between 1 and 255). The meaning of the time-to-live is network specific.

  • int getTimeToLive(): This method returns the current time-to-live of packets transmitted through this socket.

Other multicast-specific methods are also provided, as are methods inherited from the superclass.

Summary

To summarize, there are two ways to use these classes:

  • To transmit a UDP or multicast packet, first get an InetAddress object corresponding to the destination host or group. Then, construct a DatagramPacket containing the target host and port, and the data to be sent. Next, open a DatagramSocket. Finally, transmit your packet through the socket.

  • To receive a UDP or multicast packet, first create a DatagramPacket containing a byte array into which you will receive packets from the network. Next, open a DatagramSocket on the specific port to which the packets will be addressed. If you are receiving a reply to a transmission of yours, then you should use the same DatagramSocket that you used for the initial transmission. Next, if you will be listening to a multicast conversation, construct an InetAddress for the appropriate group, and then join that group. Finally, call receive() to wait for a packet to arrive. When it does, you can query the DatagramPacket for details.

And, as always, when you are finished with your network connection, close the DatagramSocket so that your operating system network resources can be freed up.

:END_SIDEBAR

It is through execution of the algorithm that Merlin achieves consciousness. He's also lead author of Java Network Programming, Second Edition, in all good bookstores now.

Learn more about this topic

1 2 3 Page 3
Page 3 of 3