October 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Dynamic Service Discovery with Java

  • February 19, 2008
  • By Rob Lybarger
  • Send Email »
  • More Articles »

Something for Nothing?

It is rightly said you cannot get something for nothing. Those of you who have worked with multicast network services know that there isn't just one magic channel that all the multicast traffic goes out on—you still have to co-ordinate the server and client to use the same multicast address and port. Because this still must be kept in sync, it is indeed worth asking whether you have gained anything or merely moved the mountain (slightly). Here's where the idea pays off: Even though different service applications will be running on different ports, the service discovery code only needs to run on one agreed-upon multicast channel. In other words, Although you have shifted the mountain slightly, you only really need to climb up it one time. (Furthermore, the same service cannot run on the same port inside the same machine... however, any number of programs can join the same multicast channel. This lets each service instance run on any port available, again without needing to coordinate what those port numbers actually are.)

To be fair, maximum pay-off only comes if there is a system-wide service running in the machine that all applications (server- and client-side) can interact with. Each server-side application talks to this service and registers itself when it starts up, handing over its instance name and connection parameters (and unregistering when it stops running). The requests that come in over the network are actually responded to via this system-global service. (This is what Bonjour and various work-alikes are.) Your own project might not be of sufficient importance to install and run your own operating system service. (Then again, maybe it is, but my own project wasn't.) However, if you trade down a little bit, each service instance can easily run its own query responder on an agreed-upon multicast channel. This still allows for the actual service itself to run on whichever dynamic port is available (or whichever one suits the whims of whomever installs and runs it) while allowing for clients to find the service without any advanced knowledge. You really do seem to get something for nothing out of the deal.

As is usually the case when dealing with connectionless datagram packets in general, the devil is in the (implementation) details. If a service runs its own responder, that will definitely need to exist in its own thread. Likewise, the clients need a little co-ordination to "wait a second or so" when gathering replies (if any) and possibly add a little prompt to involve the user if multiple replies do come back. I will not be discussing how to handle the user interface side of the client. (However, you might want to refer to another article of mine that discusses coordination of tasks in a Swing application.)

Details

By way of a reminder, this "automagic" service discovery technique employs the use of a multicast address. The address range 224.0.0.0 through 239.255.255.255 (or alternately, 224.0.0.0/4) is a special range of IP addresses set aside for multicast use. They cannot be assigned to a specific host. (This being said, these addresses still have ports.) Furthermore, a handful of addresses in the 224.0.0.x address space are already reserved for specific use. You can get some good information and links by reading this wikipedia entry. You should also refer to the Java API documentation for the MulticastSocket class. (The Socket and ServerSocket classes are probably more familiar, but those are not used for multicast traffic.) Also, you might want to refer to the Java API documentation for the DatagramPacket class, which forms the transmission container for UDP data.

Just to give a brief example, multicast networking code looks as follows:

...
MulticastSocket socket = new MulticastSocket(9999);
InetAddress address = InetAddress.getByName(230.0.0.1);
socket.joinGroup(address);
...
DatagramPacket inboundPacket, outboundPacket;
...
socket.receive(inboundPacket);
...
socket.send(outboundPacket);
...

The sample code I will be working with uses the 230.0.0.1 address and the 4321 port. This is a rather arbitrary choice, and can easily be changed as needed. Also, instead of giving a step-by-step explanation of my particular implementation for the server-side responder code and client-side browser code, I will rather be showing a simple "time server" application and client, and then the steps needed to connect both sides to the provided implementation. It is left as an exercise to the reader to poke around in source code, but the biggest pieces involve rather vanilla networking and threading code.

Time Server Example: Starting Point

Just to keep the demo simple, consider you want to write a TCP-based network service that, when connected to, immediately emits the machine's current time to the client and then disconnects. This is rather elementary networking code on both sides. First, the server:

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class TimeServer {
  public static void main(String[] args) {

    ServerSocket serverSocket = null;

    /* section one */
    try {
      serverSocket = new ServerSocket();
      serverSocket.bind(
            new InetSocketAddress(
                  InetAddress.getLocalHost(),
                  9999));
    }
    catch (IOException ioe) {
      System.err.println(
            "Could not bind a server socket to port 9999: "+ioe);
      System.exit(1);
    }

    /* section two */
    System.out.println("Server is now taking connections...");
    while (true) {
      try {
        Socket socket = serverSocket.accept();
        System.out.println("Connection from: "+
                           socket.getInetAddress());
        OutputStreamWriter writer = new OutputStreamWriter(
              socket.getOutputStream());
        writer.write(new Date().toString()+"\r\n");
        writer.flush();
        socket.close();
      }
      catch (IOException ie) {
        System.err.println("Exception: "+ie);
      }
    }

  }

}




Page 2 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel