To Cliser Home

Cliser Example: A Knock-Knock Service Using TCP and Java

To Cliser
Architecture

To illustrate the construction of a non-standard TCP/IP service in Java, this example builds a server that delivers "knock-knock" jokes, and a client to access the server, both using the TCP protocol.

Our "knock-knock" server will listen for client requests on port 1234. For those who are unaware or who have managed to block out their memories of this genre of "lame" jokes, the "knock-knock protocol" goes something like this:

  Server: "Knock knock" 
  Client: "Who's there?" 
  Server: "Wendy" 
  Client: "Wendy who?" 
  Server: "Wendy wind blows de cradle will rock" 

A knock-knock server thus waits for a connection, to which it replies with the string Knock knock. It waits for the client to respond with Who's there?, to which it responds with some first-part, usually a name. It waits for the client to respond with first-part who?, to which it responds with a last-part, which involves some semi-humorous word-play on first-part.

To satisfy its side of the "knock-knock protocol", a knock-knock client begins by connecting to the server, and then waiting for the server to send it the string Knock knock. I then displays that string, displays and replies with Who's there?, waits for and displays the server's response, displays and replies with the server's response followed by who?, and lastly gets and displays the server's final response.

To build a client and server for the daytime service, we follow the steps described previously:

1. Use Cliser

1a. Run Cliser.

As in our other examples, we run the Cliser graphical user interface by entering:
  $ java cliser.GUI
This displays the Cliser graphical user-interface, as shown below:

1b. Enter Parameters.

We then enter the name of our service (KnockKnock), the port is should use (1234), and the protocol we want to use (TCP, since the "knock-knock protocol" requires the exchange of several messages, which implies the use of a sustained connection; indicate what we want to generate ( both a client and a server), specify the source language we want to generate (Java), and the kind of server we want (Concurrent/multi-threaded, since we don't want our server to be blocked by a slow client):

1c. Generate Source Code.

In the GUI interface, we then click the Generate button. This generates the files KnockKnockTCPClient.java and KnockKnockConcurrentTCPServer.java. We then click the Quit button to quit using Cliser.

2. Customize the Source Code

Since we are building both a client and a server, we must customize both of the files generated in the previous step.

2a. Customize the Server

Using our favorite text editor, we open the file KnockKnockConcurrentTCPServer.java and replace the comments in method interactWithClient() with statements that provide the required behavior, adding attribute variables as necessary. We add three attribute variables:
  private static Random myGenerator = new Random();

  private final static String [] FIRST_PART
                                 = { "Hatch",
                                     "Boo",
                                     "Cows go",
                                     "Doris",
                                     "Dwayne",
                                     "Max",
                                     "Police",
                                     "Wendy",
                                     "Wild Tom of the One-Eyed Left-Handed Monkey Boys"
                                   };
  private final static String [] LAST_PART 
                                 = {"Gesundheit!",
                                    "Don't cry, it's only a joke.",
                                    "No, cows go 'moo', owls go 'who'!",
                                    "Doris locked, that's why I had to knock!",
                                    "Dwayne the bathtub, I'm dwowning!",
                                    "Max no difference -- open ze door.",
                                    "Police open the door -- it's cold out here!",
                                    "Wendy wind blows, de cradle will rock!",
                                    "Well how many 'Wild Tom of the One-Eyed Left-Handed Monkey Boys' do you know?"
                                  };
The first attribute is a variable to generate random integers. The second attribute is a constant array of strings, whose elements are the first parts of knock-knock jokes. The final attribute is a constant array of strings whose elements are the last parts of knock-knock jokes. Since we are using a multi-threaded server, we define these attributes as class variables and constants of class KnockKnockThread, so that each thread can share the same attributes.

Given these attributes, we can replace the comments in method interactWithClient() with the following statements:

  public void interactWithClient()
  {
    int randomIndex = myGenerator.nextInt(FIRST_PART.length);
    this.send("Knock knock");
    String reply1 = this.receive();
    if ( reply1.equalsIgnoreCase("Who's there?") )
    {
      this.send(FIRST_PART[randomIndex]);
      String reply2 = this.receive();
      if ( reply2.equalsIgnoreCase(FIRST_PART[randomIndex] + " who?") )
         this.send(LAST_PART[randomIndex]);
      else
         this.send("***'" + FIRST_PART[randomInt] + " who?' expected"); 
    }
    else
      this.send("'Who's there?' expected");
  }
That's it! While not quite trivial, our server customization is this simple because the Cliser system architecture provides the easy-to-use, protocol-independent send() and receive() communication primitives.

Note that a Cliser server or thread using TCP can immediately interact with its client, because a connection has already have been accepted prior to the execution of interactWithClient().

To use Java's Random class, we must add the line

  import java.util.Random;
to the other import statement at the beginning of the file. The result is the file KnockKnockConcurrentTCPServer.java.

2b. Customize the Client

Again using our favorite text editor, we open the file KnockKnockTCPClient.java and replace the comments in the body of method interactWithServer() with statements that define the required behavior. For simplicity, we will make a non-interactive, text-based client; converting it to an interact and/or GUI client is left as an exercise:
  public void interactWithServer()
  {
    String received1 = this.receive();
    if ( received1.equalsIgnoreCase("Knock knock") )
    {
      System.out.println("Server: " + received1);
      String sent1 = "Who's there?";
      System.out.println("Client: " + sent1);
      this.send(sent1);
      String received2 = this.receive();
      System.out.println("Server: " + received2);
      String sent2 = received2 + " who?";
      System.out.println("Client: " + sent2);
      this.send(sent2);
      String received3 = this.receive();
      System.out.println("Server: " + received3);
    }
    else
    {
      System.err.println("KnockKnockTCPClient.interactWithServer(): "
                         + "'Knock knock' expected, "
                         + received1 + "' received");
      System.exit(1);
    }
  }
The result is the file KnockKnockTCPClient.java. Once again, our customization is quite easy thanks to Cliser's simple send() and receive() communication primitives.

3. Compiling

Once we have customized the files generated by Cliser, we can compile each as we would any other Java file:
  $ javac -deprecation KnockKnockTCPClient.java
  $ javac -deprecation KnockKnockConcurrentTCPPServer.java
These commands create the files KnockKnockTCPClient.class, and KnockKnockThread.class and KnockKnockConcurrentTCPServer.class, respectively. Because Cliser-generated clients and servers are derived from classes in the Cliser class library you must have this library installed on your machine for such clients and servers to compile and run correctly.

4. Deploy and Run

To deploy your client and server, copy their .class files to a directory/folder that is in your CLASSPATH.

4a. Running the Server

You can then run the server (as the system administrator) as follows:
  $ java KnockKnockConcurrentTCPServer
This will start our server running, listening for connections on port 1234. The server can also be run on a different port by specifying that port as a command-line argument.

4b. Running the Client

If your server is running on the host myMachine.myDomain, you can run your client by giving that host as a command-line argument:
  $ java KnockKnockTCPClient myMachine.myDomain 
Here is a sample run of our client:
Server: Knock knock
Client: Who's there?
Server: Dwayne
Client: Dwayne who?
Server: Dwayne the bathtub, I'm dwowning!


Up to the Cliser home page Up to the Examples A UDP Client/Server for the standard Daytime Service using Java A TCP Client/Server for the standard Echo Service using C++ over BSD A TCP Client/Server for the standard Echo Service using C++ over WinSock A UDP Client/Server for a Random Quotes Service using C++ over TLI


This page maintained by Joel Adams.