Sender | Receiver |
(S1) Create a transport profile (S2) Create a channel and fill out channel information:
(S4) Create a multicast socket (with transport profile) (S5) Send data (S6) Close socket |
(R1) Listen for channel advertisements (R2) For each advertised channel, check if it is of interest (R3) For the selected channel:
(R5) Receive data (R6) If SessionDoneException detected, close socket |
We refer to steps 1-4 as the set-up phase, and step 6 as the tear-down phase.
There are considerable details and variations: for example, what is in the transport profile? Is the data sent as a stream, or in logical blocks? Is data sent continuously (file transfer) or with interleaving gaps (publish and subscribe)? When does the sender stop advertising? We will try to cover some of these below.
import com.sun.multicast.allocation.MulticastAddressManager; import com.sun.multicast.reliable.channel.ChannelManagerFinder; import com.sun.multicast.reliable.channel.PrimaryChannelManager; import com.sun.multicast.reliable.channel.Channel; import com.sun.multicast.reliable.transport.TransportProfile; import com.sun.multicast.reliable.transport.tram.TRAMTransportProfile; import com.sun.multicast.reliable.transport.RMPacketSocket; import java.net.InetAddress; import java.net.DatagramPacket;TRAM is used here as an example transport; JRMS supports multiple transports. Documentation on the com.sun.multicast classes can be found from the JRMS javadoc pages.
com.sun.multicast.allocation | multicast address management |
com.sun.multicast.advertising | advertising facilities |
com.sun.multicast.reliable | general JRMS exceptions |
com.sun.multicast.reliable.channel | channel interfaces, classes, and events |
com.sun.multicast.reliable.sample_code | documentation and source code for several sample applications |
com.sun.multicast.reliable.simple | basic objects for simple applications |
com.sun.multicast.reliable.transport | top-level transport interfaces |
com.sun.multicast.reliable.transport.lrmp | LRMP transport |
com.sun.multicast.reliable.transport.tram | TRAM transport |
com.sun.multicast.reliable.transport.um | Unreliable multicast transport |
com.sun.multicast.util | JRMS utilities |
private TransportProfile tp; private InetAddress addr; private int port; private Scope scope; // first, allocate one multicast address MulticastAddressManager mam = MulticastAddressManager.getMulticastAddressManager(); if (scope == null) { scope = mam.getScopeList(IPv4AddressType.getAddressType()).findScopeForTTL(ttl); if (scope == null) throw new IOException("No scope for requested TTL"); } /* get an address with a long duration */ Lease lease = mam.allocateAddresses(null, scope, (int) ttl, 1, startTime, startTime, duration, -1, null); InetAddress addr = ((IPv4Address) lease.getAddresses(). getFirstAddress()).toInetAddress(); // create a TRAM transport profile // other transports, such as UM, LRMP, can be used instead of TRAM tp = new TRAMTransportProfile(addr,port); // set multicast session scope tp.setTTL(ttl); // enable ordered delivery tp.setOrdered(true); // set maximum rate of data transfer tp.setMaximumSpeed(speed); // and set any other transport-specific parameters ...
// create a channel private PrimaryChannelManager pcm; private Channel channel; pcm = ChannelManagerFinder.getPrimaryChannelManager(null); channel = pcm.createChannel(); channel.setChannelName(channelName); channel.setApplicationName(applicationName); channel.setTransportProfile(tp); channel.setAbstract(someBlurbAboutThisChannel); channel.setAdvertisingRequested(true); // plus any other channel parameters ... // once enabled, configuration is complete and advertisement can start channel.setEnabled(true);Note, the parameter passed to getPrimaryChannelManager is a reference to the primary channel manager. When null is specified, the local channel manager is returned. These code fragments illustrate steps (S2) and (S3). Note the lines setTransportProfile and setAdvertisingRequested; the former provides the transport mechanism, and the latter gets the channel advertised so that receivers can find out about it.
// find the channel of interest private PrimaryChannelManager pcm; private Channel channel; pcm = ChannelManagerFinder.getPrimaryChannelManager(null); // channel lookup is by channelName and applicationName // since more than one may match, need to determine the right one long channelids[] = pcm.getChannelList(channelName, applicationName); // loop through the following to find the channel of interest channel = pcm.getChannel(channelids[i]); // apply application-specific criteria checkInterest(channel); ...In order to receive data, the application needs the transport profile:
// extract the transport profile from the advertised channel tp = channel.getTransportProfile();The transport profile that was originally created by the sender has been made part of the channel which through advertisement became known to the local channel manager. This explains how the receiver gets hold of the sender's transport profile. The above code fragments illustrated the receiver's set-up phase, steps (R1)-(R3).
private RMPacketSocket ms; private DatagramPacket sendPacket; byte[] senddata = new byte[PACKET_SIZE]; // create socket ms = channel.createRMPacketSocket(tp, TransportProfile.SENDER); // or directly using tp // ms = tp.createRMPacketSocket(TransportProfile.SENDER); // data transmission // repeat the following according to application needs preparePacket(senddata); sendPacket = new DatagramPacket(senddata, senddata.length); ms.send(sendPacket); ... // teardown phase ms.close(); // stop advertising this channel // this can be done earlier, e.g. after data transmission starts channel.setAdvertisingRequested(false);Note there are a couple of ways to create a socket, either using the channel's createRMPacketSocket method, or using the transport profile's createRMPacketSocket method. Usually, the sockets are created using channel methods. Channels provide more services to applications, such as security and filtering mechanisms. A very simple application that does not need channel features can use methods of the transport profile to create sockets. This is what the receiver does:
private RMPacketSocket ms; private DatagramPacket recvPacket; // create socket ms = channel.createRMPacketSocket(tp, TransportProfile.RECEIVER); // data reception // loop through the following until end of transmission recognized try { recvPacket = ms.receive(); // application-specific processing consumePacket(recvPacket); } catch ( SessionDoneException e ) { // teardown phase ms.close(); } catch ( JRMSException e) { System.out.println("..."); }Note, the use of exceptions to dispatch different return conditions is a very basic Java feature. For the sake of simplicity, we have not used exceptions in the example code fragments, except in the above case where we have illustrated the use of a couple of them. The JRMS API contains a set of common exceptions supported by its implementations.
// create stream socket ms = channel.createRMStreamSocket(tp, TransportProfile.SENDER); // or directly // ms = tp.createMRStreamSocket(TransportProfile.SENDER); OutputStream s = ms.getOutputStream(); // data transmission // use whatever stream write method that's convenient, e.g. s.write("hello"); ... // send s.flush(); // teardown phase ms.close(); channel.setAdvertisingRequested(false);Equivalently, at the receiver:
// create stream socket at receiver ms = channel.createRMStreamSocket(tp, TransportProfile.RECEIVER); // or directly // ms = tp.createMRStreamSocket(TransportProfile.RECEIVER); InputStream s = ms.getInputStream(); // data reception // use whatever read method that's convenient byte message[] = new byte[SIZE]; int len = s.read(message); ... // teardown phase ms.close();
This section explains various manual configuration tips when it is desirable/necessary to override autoconfiguration.
// Get a LocalPCM LocalPCM m = (LocalPCM) ChannelManagerFinder.getPrimaryChannelManager(null); // Create a serializable channel (LocalChannel) and fill in the // parameters, including the transport profile LocalChannel c = (LocalChannel) m.createChannel(); c.setChannelName(channelName); c.setApplicationName(applicationName); InetAddress mcastAddress = InetAddress.getByName(address); tp = new TRAMTransportProfile(mcastAddress, port); tp.setMaxDataRate(speed); tp.setTTL(ttl); tp.setOrdered(true); c.setTransportProfile(tp); c.setEnabled(true); // Use the LocalPCM fileChannel method to store the channel in // a file. Here, the ChannelName is used as the file name. m.fileChannel(c, channelName);And this is how you read it back:
LocalPCM m = (LocalPCM) ChannelManagerFinder.getChannelManager(null); LocalChannel c = (LocalChannel) cm.readChannel(channelFileName); TransportProfile tp = c.getTransportProfile();For many situations, it is convenient to create a channel once and use it again and again. For example, your application distributes daily news continuously. The sender and receivers can be restarted at different times and it is not convenient to use SAP. Then a channel can be created and posted on a web page (or a well-known directory if file sharing is available). Both the sender and receivers read the channel from the same place. The side-effect, in this case, is that the different sessions will have the same session Id.
In the simple package, there is a SimpleChannel program that lets you create a simple channel with a few of the essential parameters that you can enter on the command line.
tp.setTreeFormationPreference(TRAMTRansportProfile.TREE_FORM_HAMTHA_STATIC_R)This tells all the receivers that the session is using HAMTHA method for for tree formation, and reading a static file is allowed. (HAMTHA stands for "Head Advertisement before data starts and Member Triggered Head Advertisement after data started", which is the default method).
[head's ip address] [ttl from head]The ip address is in the a.b.c.d form, and the ttl is the number of hops (in terms of multicast) the receiver is away from the head (e.g. equal to 1 if both hosts are on the same LAN). If you allow a number of possible repair heads, you can include a number of lines in the file.
[local host's ip address] 0If this receiver must have a specific parent, then it must contain an additional line as described above.
com.mycompany.newprotocolName
Transport name and version Multicast Address Multicast Port Ordered Delivery Available MultiSender Support Available AuthenticationSecurity Specifications Maximum Data RateWhile the names of these parameters may be long externally, it is helpful to use shortened variable names internally. The reason is that these names get carried in the serialized form of the transport profile sent in channel advertisements. Using shorter variable names helps avoid exceeding the packet size limit imposed by the advertising protocol. The following is an example definition:
private int mr = DEFAULT_MAX_RATE; public int getMaximumDataRate() { return mr; } public void setMaximumDataRate(int rate) { mr = rate; }