Chapter 17. Servers IV—beyond unicast

  • Datagrams—Multicast—Broadcast—Clients—Exercises

In this chapter

So far in this book we have encountered quite a few types of RMI server: standard unicast servers, activatable servers, RMI/IIOP servers, dual-protocol servers, and secure servers. All these servers are “unicast” servers—they use point-to-point connected streams for their communications.

This chapter describes other plausible types of RMI server, based on datagrams instead of streams. The chapter is largely motivated by the frequent beginner's question “what is unicast? and what other kinds of -cast are there?” The chapter is included for completeness. We do not know of any plans by Sun or anyone else to offer the server types described in this chapter, and their utility or otherwise in an RMI framework is a subject of much discussion.[1]

Datagrams

A datagram is a single transmission which may be delivered zero or more times; whose sequencing with respect to other datagrams between the same two endpoints is not guaranteed; and which is subject to size limitations.[2] Datagrams are a peer-to-peer mechanism, not a client-server mechanism. There is no such thing as a “passive” datagram socket in a “listening” state. Endpoints in a datagram exchange are not explicitly connected or disconnected, and connections are not explicitly accepted. In the IP protocol suite, datagrams are supported by UDP. In Java, datagrams are implemented by the java.net.Datagram class, and are sent and received over sockets of the class java.net.DatagramSocket.

A datagram server would be a server whose communications use datagrams rather than streams, and datagram sockets rather than stream sockets (java.net.Socket). In both these respects a datagram server would differ from all the RMI server types discussed so far in this book.[3]

The difference between stream sockets and datagram sockets is the difference between TCP and UDP, the protocols which they “speak”. TCP implements a reliable connected stream, over which any number of bytes of data can be sent and received by each end of the connection. The costs of this reliability are not insignificant, as the underlying implementation has to perform quite a number of hidden tricks to achieve it:

  • window negotiation

  • pacing

  • sequencing

  • acknowledgement

  • retransmission on error

  • a three-way handshake to establish a connection

  • a four-way handshake to close a connection.

TCP is a suitable protocol for large-scale, error-free data transfer, and for multiple interactions between client and server. In some respects, TCP is not an ideal protocol for transactions, because of the overheads of setting up and tearing down the connection, and negotiating transfer parameters. RMI amortizes this overhead over multiple remote method invocations where possible, by conserving server sockets and client connections: this has a large effect when multiple calls are made to the same host and port over a short time interval.

UDP provides datagrams, over datagram sockets. UDP is a simple protocol whose overheads are insignificant.

UDP, being a “connectionless” protocol, has no connection/disconnection handshake, no implicit delivery windowing, no pacing, no acknowledgements, and no retransmissions.

Purpose

If the severe message size limitations of UDP can be tolerated, there are theoretical efficiency gains to be had by using UDP. A datagram client which sends and receives single packets per RMI call doesn't incur the three-way connect and four-way disconnect of TCP, its window-size negotiation, or its slow-start behaviour. A UDP client only needs as many datagram sockets as it has simultaneous threads performing RMI calls; a TCP client needs as many sockets as it has distinct {host, port} pairs to be connected to.

There can also be distinct design advantages in exposing to the application the error situations handled by TCP but not by UDP, including especially receipt of a duplicate message by the server, and non-receipt of an acknowledgement by the client. This is particularly so in transaction-oriented communications.[4]

Semantics

The normal semantics of an RMI call are that the call has been executed “at most once”. At the transport level, a remote call sent over a datagram may yield zero or more replies, so the semantics of a datagram remote call are that the call has been executed “at least zero times”. This difference is significant, and it has considerable implications for the design of clients and servers.

It also has a major implication on the RMI design for datagrams. An RMI call to a datagram server needs to be retried at appropriate intervals if a reply isn't received within the expected time, and duplicate replies need to be discarded. The number of retries, or perhaps the total elapsed time, should be bounded.

What the “expected time” might be is another matter: it depends on a number of factors, including network bandwidth, network load, server load, the time the remote call should take to execute, and so forth. The “appropriate interval” is yet another matter: strictly speaking, it should be subjected to TCP's “exponential backoff” (for example, doubled at each failure), rather than just retrying the call at fixed intervals.

Mechanisms to control all this, preferably on a per-method basis, would need to be available to clients. These should cater for the special case where the client doesn't want the API retried at all and doesn't want the reply to be waited for, in other words a pure one-way datagram message or asynchronous call. The mechanisms might be static methods in a service class, or configuration files.

Example

One example of a service which would function at least as well via UDP datagrams as via TCP streams is the RMI registry. The registry typically runs in its own process on a dedicated socket, in which case it doesn't participate much in RMI's conservation of server sockets and client sessions. Once clients have accessed it (to bootstrap their server references) they typically don't go back to it again. This “one-shot” access pattern is typical of UDP-style services.

Implementation

There are at least two ways to implement an RMI datagram server.

One way is to provide implementations of RMI ServerSocketFactory and RMI ClientSocketFactory which provide instances of ServerSocket and Socket respectively which are “backed” by DatagramSockets. This is simply an exercise in mapping the Socket and ServerSocket methods on to the facilities of DatagramSockets, in other words wrapping a DatagramSocket inside a proxy which appears to be a Socket or ServerSocket.

Another way is to make a number of enhancements to RMI:

  • Introduce a new abstract base class, say DatagramRemoteObject. This class would need a DatagramRemoteObject.exportObject method which exported the object via a new or existing DatagramSocket.

  • Provide a UDP version of the RMI transport. This would create a thread for each distinct DatagramSocket, to listen at the socket and dispatch remote calls packaged in incoming datagrams. The UDP infrastructure would also support unmarshalling a remote call from datagrams received, and marshalling the result or exception of the call to a reply datagram. The UDP transport would also implement the semantics required of the call—timeout, retry interval, maximum number of retries or total elapsed time in the presence of failures, as described above.

  • Export a remote stub containing a different kind of RemoteRef, using the UDP transport to communicate with the target, instead of the TCP transport.

  • Design and implement a solution to allow socket factories to be defined which return DatagramSockets instead of Sockets and ServerSockets. This is left as an exercise for the reader.

Implementation of DatagramRemoteObject and a UDP transport layer for RMI is not an unduly difficult exercise, except for an implementation issue in the Sun code which could initially be resolved by excluding support for DGC.

Support for a datagram server would exclude HTTP tunnelling: HTTP doesn't operate over UDP.

Multicast

A multicast is a datagram sent to a multicast group over a multicast socket. A multicast socket is a datagram socket with additional capabilities for joining “groups” of other multicast hosts. In the IP protocol suite, multicast is supported by UDP and the Internet Group Management Protocol (IGMP). A multicast group has a distinct IP address of its own.[5] Datagrams can be sent to individual IP addresses or to an IP multicast group.

An RMI multicast server would be a further development of the datagram server described above. It would communicate via a java.net.MulticastSocket instead of a java.net.DatagramSocket.

Purpose

Multicast is increasingly used over parts of the Internet for purposes such as mirroring Web sites, video-conferencing, replicated services, collaborative computing, streaming audio and video, and real-time data delivery.

An RMI multicast object would be a member of a multicast group. It would be capable of receiving transmissions directed straight at it, or directed at the group. Within the group, a multicast object would most probably function as a replicated or federated service. Instances of the same multicast object might execute at each host in the multicast group. All instances would receive all RMI calls directed at the group. Members of the group may also communicate with each other via the multicast mechanism. Multicast uses the time-to-live (TTL) feature of UDP to limit the scope of each multicast to a chosen neighbourhood. Scoped intra-group communications may be used for synchronization purposes.

Semantics

The semantics of a multicast remote call are similar to those of a datagram remote call—the call has been executed “at least zero times”—except that replies may be received from every member of the multicast group, and the replies may not all be the same. Some may be successes (return values) and some may be failures (remote exceptions). Alternatively, a multicast object might suppress failure replies altogether, like the broadcast servers discussed below.

Clients of multicast objects are in much the same position as clients of a datagram server. They are more likely to receive multiple replies to a single request, as there is likely to be more than one member of the multicast group. Without using higher-level protocols, there is no reliable way of determining how many members of a multicast group exist. Therefore, multicast clients don't know how many replies to expect, so there is no way they can implement multicast-specific policies such as requiring all replies to be received, requiring all replies to be identical, or even the much weaker policy of requiring at least one reply indicating success. It would make some sense to constrain remote multicast methods from returning results at all, i.e. to have a return type of void.

This in turn constrains the uses to which RMI multicast objects can be put. The most apparent use seems to be to provide replicated and federated services. The Jini Discovery Service uses IP multicasting, but not over RMI.

For a fuller consideration of multicasting see the paper on the Java Reliable Multicast Service (this is a Sun research project, not a product).[6] This paper contains an interesting discussion covering the following issues:

  1. Number of senders: is more than one sender to be supported?

  2. Late joins: is it permitted to join a multicast group after senders have started sending? If so, what needs to happen?

  3. Real-time: does the application require real-time performance?

  4. Consistency: must all data be delivered to all receivers at exactly the same time? or only that at some time? is it sufficient that some application gets the data some time? or do transactional checkpoints exist?

  5. Ordering: preservation of the order of sends is usually, but not always, required

  6. Reliability: most applications require zero data loss, but some (e.g. streaming audio or video) do not.

Higher-level protocols layered over IP multicast have been proposed which address a number of the issues raised in this section. These include Tree-Based Reliable Multicast (TRAM), Lightweight Reliable Multicast Protocol, and the Session Announcement and Description protocols.[7] A realistic implementation of multicast RMI would allow such protocols to be incorporated into the server's operation, by means of an extensible super-protocol framework to allow for future multicast protocol developments, as in the Sun JRMS research project described above.

The effort that would be needed to shoehorn multicasting into the RMI framework exposes the limitations of the RMI model: single client, single server, single request, single response, and synchronous behaviour. This is a limited way to use a network. Multicasting is not really like calling methods at all. It is much more like sending and receiving asynchronous events. A realistic application of multicasting would be a server discovery service replacing the registry, as seen in the Jini Discovery and Lookup Service.

Implementation

As with datagram servers, the implementation of an RMI multicast server could be tackled in two ways: (a) via socket factories which deliver Socket-like proxies for datagram sockets as above, and ServerSocket-like proxies for multicast sockets; or (b) by making a number of enhancements to RMI. This section outlines the second of these techniques.

First, we would need a new abstract base class called, say MulticastRemoteObject. This class would probably be derived from the DatagramRemoteObject class described above. It would need an implementation of MulticastRemoteObject.exportObject which exported the object via a new or existing MulticastSocket. This class would also need an API for joining and leaving multicast groups: alternatively, perhaps an argument to the exportObject and MulticastRemoteObject.unexportObject methods might be specialised for this use.

Second, we would need to revise the UDP transport described above for multicast sockets. Actually, few if any changes would be required to the datagram transport, largely because MulticastSocket is derived from DatagramSocket.

Third, we would need to create the same kind of remote stub as for the datagram server described above. (The client of a multicast server uses a DatagramSocket to communicate with it; it doesn't need to use a MulticastSocket.)

Broadcast

A broadcast is a datagram sent to a “broadcast address”. A broadcast address is an IP address whose host portion is all 1's, meaning all hosts in the subnet: in a Class C subnet, whose netmask is 255.255.255.0, the broadcast address would be A.B.C.255, where A, B, and C are the Class A, Class B, and Class C parts of the network address.

An RMI broadcast server would be a small further development of the datagram server described above.

Purpose

An RMI broadcast server is largely indistinguishable from a datagram server. It would function most probably as a replicated server in a Class C or smaller subnet. Instances of the same RMI remote object would be executing at several hosts in the subnet. All instances would receive RMI calls directed at the subnet.

Obviously UDP broadcasts are cheap to implement but rather drastic in their effect on the network. The UDP Multicast facilities have largely superseded the functions of UDP broadcasts.

Semantics

The semantics of a broadcast remote call are similar to those of a datagram remote call—the call has been executed “at least zero times”—except that replies may be received from every member of the subnet.

Most probably, an RMI broadcast server would adopt the semantics of not replying at all on failure, only replying on success, following the example of the 1983–1984 Sun RPC package.

Implementation

The only difference between a datagram server and an RMI broadcast server is the fact that the latter is addressed by a broadcast address rather than a specific host address. All that an implementation would require would be a way of getting the broadcast address into the stub. The elaboration of this is left as an exercise for the reader.

Clients

It is important to note that apart from the addition of some call-controlling mechanism, the clients of the servers described in this chapter would be largely oblivious to the server type. The reason for this is that in RMI “the stub is the protocol”. Once a client has acquired a remote stub it just executes method calls. It doesn't have to be at all aware of the protocol implemented by the stub.

Exercises

1:

Design a DatagramRemoteObject class, including methods to control timeouts and retries.

2:

Design a BroadcastRemoteObject class, including methods to control timeouts and retries.

3:

Design a MulticastRemoteObject class, including methods to control joining and leaving multicast groups, and to control the TTL of messages.



[1] For example, see several exchanges on the RMI Mailing List with “datagram” or “UDP” in the subject.

[2] UDP datagrams are limited by the protocol to 65507 bytes in length, and by most implementations to 8K bytes. IPv6 has “Jumbograms” at the IP level; this allows UDP datagram sizes up to 232 –1: see IETF RFC 2675. Practical exploitations of UDP often restrict messages to 512 bytes—a single IP packet—to avoid fragmentation problems in network routers.

[3] Strictly speaking there shouldn't even be datagram clients or servers, just peers.

[4] For a detailed discussion of UDP versus TCP see Stevens, Unix Network Programming, Volume I, §§2.3, 2.4, and 20, especially §20.4.

[5] In IPv4 its first four bits are 1110; in IPv6 its high-order byte is FF.

[6] Sun Microsystems Laboratory Technical Report TR-98-68.

[7] Liao, Light weight Reliable Multicast Protocol as an Extension to RTP; Handley and Jacobson, SDP: Session Description Protocol, RFC 2327; Chiu, Hurst, Kadansky, and Wesley, TRAM: a Tree-based Reliable Multicast Protocol.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.178.151