Skip to content

9. Program source code

9.1. The generic TCP client

Many services created at the dawn of the Internet operate according to the echo server model studied previously: client-server exchanges consist of exchanging lines of text. We will write a generic TCP client that will be launched as follows: java cltTCPgenerique server port

This TCP client will connect to the server's port. Once connected, it will create two threads:

  1. one thread responsible for reading commands typed on the keyboard and sending them to the server
  2. a thread responsible for reading the server's responses and displaying them on the screen

Why two threads? Each TCP-IP service has its own specific protocol, and the following situations sometimes arise:

  • the client must send several lines of text before receiving a response
  • a server’s response may contain multiple lines of text

Therefore, the loop that sends a single line to the server and receives a single line from the server is not always suitable. We will therefore create two separate loops:

  • a loop to read commands typed on the keyboard to be sent to the server. The user will signal the end of commands with the keyword "fin".
  • a loop to receive and display the server’s responses. This will be an infinite loop that will only be interrupted by the server closing the network connection or by the user typing the “end” command at the keyboard.

To have these two separate loops, we need two independent threads. Let’s look at an example of execution where our generic TCP client connects to an SMTP (Simple Mail Transfer Protocol) service. This service is responsible for routing email to its recipients. It operates on port 25 and uses a text-based exchange protocol.


Dos>java genericTCPclient istia.univ-angers.fr 25
Commands:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test

line1
line2
line3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[end of thread reading server responses]
end
[end of the thread sending commands to the server]

Let's comment on these client-server exchanges:

  • The SMTP service sends a welcome message when a client connects to it:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • Some services have a "help" command that provides information on the commands available for that service. That is not the case here. The SMTP commands used in the example are as follows:
    • mail from: sender, to specify the sender’s email address
    • rcpt to: recipient, to specify the email address of the message’s recipient. If there are multiple recipients, the rcpt to: command is repeated as many times as necessary for each recipient.
    • data, which signals to the SMTP server that the message is about to be sent. As indicated in the server’s response, this is a sequence of lines ending with a line containing only a period. A message may have headers separated from the message body by a blank line. In our example, we included a subject using the Subject: keyword
  • Once the message is sent, we can tell the server that we are done using the quit command. The server then closes the network connection. The read thread can detect this event and stop.
  • The user then types "end" on the keyboard to also stop the thread reading the commands typed on the keyboard.

If we check the received email, we see the following (Outlook):

Image

Note that the SMTP service cannot detect whether a sender is valid or not. Therefore, you can never trust the "From" field of a message. In this case, the sender machin@univ-angers.fr did not exist.

This generic TCP client allows us to discover the communication protocol of Internet services and, from there, build specialized classes for clients of these services. Let’s explore the communication protocol of the POP (Post Office Protocol) service, which allows us to retrieve emails stored on a server. It operates on port 110.


Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commands:
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass mypassword
<-- +OK st has 157 visible messages (0 hidden) in 11,755,927 bytes.
list
<-- +OK 157 visible messages (11,755,927 bytes)
<-- 1 892847
<-- 2 171,661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 bytes
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<--     by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<--     Mon, May 13, 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<--     by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<--     Mon, May 13, 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2                  Tel.: 0800 77 47 95
<-- Fax: (+33) 01 40 78 64 00,  Email: noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[end of thread reading server responses]
end
[end of thread sending commands to the server]

The main commands are as follows:

  • user login, where you enter your login on the machine that hosts your emails
  • pass password, where you enter the password associated with the previous login
  • list, to get a list of messages in the format number, size in bytes
  • retr i, to read message number i
  • quit, to end the session.

Let’s now explore the communication protocol between a client and a web server, which typically runs on port 80:


Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commands:
GET /index.html HTTP/1.0

<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Welcome to ISTIA - University of Angers</title>
<-- </head>
....
<-- face="Verdana"> - Last updated on <b>January 10, 2002</b></font></p>
<-- </body>
<-- </html>
<--
[end of thread displaying server responses]
end
[end of the thread for sending commands to the server]

A web client sends its commands to the server according to the following pattern:

command1
command2
...
command
[empty line]

The web server responds only after receiving the empty line. In this example, we used only one command:

GET /index.html HTTP/1.0

which requests the URL /index.html from the server and indicates that it is using HTTP version 1.0. The most recent version of this protocol is 1.1. The example shows that the server responded by sending the contents of the index.html file and then closed the connection, as we can see the response reading thread terminating. Before sending the contents of the index.html file, the web server sent a series of headers followed by an empty line:

<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>

The line <html> is the first line of the /index.html file. The preceding text is called HTTP (HyperText Transfer Protocol) headers. We won’t go into detail about these headers here, but keep in mind that our generic client provides access to them, which can be helpful for understanding them. For example, the first line:

<-- HTTP/1.1 200 OK

indicates that the contacted web server supports the HTTP/1.1 protocol and that it successfully found the requested file (200 OK), where 200 is an HTTP response code. The lines

<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html

tell the client that it will receive 11,251 bytes of HTML (HyperText Markup Language) text and that the connection will be closed once the data has been sent.

So here we have a very handy TCP client. It certainly does less than the telnet program we used earlier, but it was interesting to write it ourselves. The generic TCP client program is as follows:

// imported packages
import java.io.*;
import java.net.*;

public class genericTCPclient{

    // receives the characteristics of a service as a parameter in the form
    // server port
    // connects to the service
    // creates a thread to read commands typed on the keyboard
    // these will be sent to the server
    // creates a thread to read responses from the server
    // these will be displayed on the screen
    // the process ends when the "end" command is typed

  // instance variable
  private static Socket client;

    public static void main(String[] args){

        // syntax
        final String syntax = "pg server port";

        // number of arguments
        if(args.length != 2)
            error(syntax, 1);

        // note the server name
        String server = args[0];

        // the port must be an integer greater than 0
        int port = 0;
        boolean portError = false;
        Exception e = null;
        try {
            port = Integer.parseInt(args[1]);
        } catch (Exception e) {
            E = e;
            portError = true;
        }
        portError = portError || port <= 0;
        if(portError)
            error(syntax + "\n" + "Incorrect port (" + E + ")", 2);

        client = null;
        // there may be issues
        try{
            // connect to the service
            client = new Socket(server, port);
        } catch (Exception ex) {
            // error
            error("Unable to connect to the service ("+ server
                +","+port+"), error: "+ex.getMessage(),3);
            // end
            return;
        }//catch

        // create read/write threads
    new ClientSend(client).start();
    new ClientReceive(client).start();

        // end of main thread
        return;
    }// main

    // display errors
    public static void error(String msg, int exitCode){
        // display error
        System.err.println(msg);
        // exit with error
        System.exit(exitCode);
    }//error
}//class  

class ClientSend extends Thread {
    // class responsible for reading commands typed on the keyboard
    // and sending them to a server via a TCP client passed as a parameter

    private Socket client;    // the TCP client

    // constructor
    public ClientSend(Socket client){
        // store the TCP client
        this.client = client;
    }//constructor

    // thread's Run method
    public void run(){

        // local data
        PrintWriter OUT = null;            // network write stream
    BufferedReader IN = null;        // keyboard stream
        String command = null;            // command read from keyboard

        // error handling
        try{
            // Create network write stream
            OUT = new PrintWriter(client.getOutputStream(), true);
      // create keyboard input stream
      IN = new BufferedReader(new InputStreamReader(System.in));
            // loop for command input and transmission
            System.out.println("Commands: ");
            while(true){
                // Read command typed on the keyboard
                command = IN.readLine().trim();
                // Done?
                if (command.toLowerCase().equals("end")) break;
                // send command to server
                OUT.println(command);
                // next command
            }//while
        } catch (Exception ex) {
            // error
            System.err.println("Sending: The following error occurred: " + ex.getMessage());
        }//catch
        // end - closing the streams
        try{
            OUT.close(); client.close();
        }catch(Exception ex){}
        // signal the end of the thread
        System.out.println("[Send: end of the thread sending commands to the server]");
    }//run
}//class

class ClientReceive extends Thread{
    // class responsible for reading text lines intended for a 
    // TCP client passed as a parameter

    private Socket client;    // the TCP client

    // constructor
    public ClientReceive(Socket client){
        // store the TCP client
        this.client = client;
    }//constructor

    // thread's Run method
    public void run(){

        // local data
        BufferedReader IN = null;        // network read stream
        String response = null;        // server response

        // error handling
        try{
            // Create network read stream
            IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
            // Loop to read text lines from the IN stream
            while(true){
                // read network stream
                response = IN.readLine();
                // Is the stream closed?
                if(response == null) break;
                // Display
                System.out.println("<-- " + response);
            }//while
        } catch (Exception ex) {
            // error
            System.err.println("Reception: The following error occurred: " + ex.getMessage());
        }//catch
        // end - closing the streams
        try{
            IN.close(); client.close();
        }catch(Exception ex){}
        // signal the end of the thread
        System.out.println("[Receive: end of thread reading responses from the server]");
    }//run
}//class

9.2. The generic TCP server

Now we'll look at a server

  • that displays on the screen the commands sent by its clients
  • and sends them, in response, the lines of text typed on the keyboard by a user. It is therefore the user who acts as the server.

The program is launched by: java genericTCPserver listeningPort, where listeningPort is the port to which clients must connect. Client service will be handled by two threads:

  • one thread dedicated exclusively to reading the text lines sent by the client
  • a thread dedicated exclusively to reading the responses typed on the keyboard by the user. This thread will signal, using the `fin` command, that it is closing the connection with the client.

The server creates two threads per client. If there are n clients, there will be 2n active threads at the same time. The server itself never stops unless the user presses Ctrl-C on the keyboard. Let’s look at a few examples.

The server is running on port 100, and we use the generic client to communicate with it. The client window looks like this:


E:\data\serge\MSNET\c#\network\generic tcp client> java genericTCPclient localhost 100
Commands:
command 1 from client 1
<-- response 1 to client 1
command 2 from client 1
<-- response 2 to client 1
end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading server responses]
[end of the thread sending commands to the server]

Lines beginning with <-- are those sent from the server to the client; the others are from the client to the server. The server window is as follows:


Dos> java genericTCPserver 100
Generic server launched on port 100
Thread for reading server responses to client 1 launched
1: Thread for reading requests from client 1 launched
<-- command 1 from client 1
Response 1 to client 1
1: <-- command 2 from client 1
response 2 to client 1
1: [End of thread reading requests from client 1]
end
[end of thread reading server responses to client 1]

Lines beginning with <-- are those sent from the client to the server. Lines N: are those sent from the server to client N. The server above is still active even though client 1 has finished. We launch a second client for the same server:


Dos> java clientTCPgenerique localhost 100
Commands:
command 3 from client 2
<-- response 3 to client 2
end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading server responses]
[end of the thread sending commands to the server]

The server window then looks like this:


Dos> java genericTCPserver 100
Generic server running on port 100
Thread for reading server responses to client 1 launched
1: Thread for reading requests from client 1 launched
<-- Command 1 from client 1
Response 1 to client 1
1: <-- command 2 from client 1
response 2 to client 1
1: [End of thread reading requests from client 1]
end
[end of thread reading server responses to client 1]
Thread for reading server responses to client 2 launched
2: Thread for reading requests from client 2 launched
<-- command 3 from client 2
response 3 to client 2
2: [End of thread reading requests from client 2]
end
[end of thread reading server responses to client 2]
^C

Now let's simulate a web server by running our generic server on port 88:


Dos> java genericTCPserver 88
Generic server launched on port 88

Now let’s open a browser and request the URL http://localhost:88/exemple.html. The browser will then connect to port 88 on the localhost machine and request the /example.html page:

Image

Now let’s look at our server window:

Dos>java genericTCPserver 88
Generic server launched on port 88
Thread for reading server responses to client 2 launched
2: Thread for reading requests from client 2 launched
<-- GET /example.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--

This reveals the HTTP headers sent by the browser. This allows us to gradually learn about the HTTP protocol. In a previous example, we created a web client that sent only the GET command. That was sufficient. Here we see that the browser sends other information to the server. This information is intended to tell the server what type of client it is dealing with. We also see that the HTTP headers end with an empty line.

Let’s craft a response for our client. The user at the keyboard is the actual server here and can manually craft a response. Recall the response sent by a web server in a previous example:

<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>

Let's try to provide a similar response:

...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2: HTTP/1.1 200 OK
2: Server: generic TCP server
2: Connection: close
2: Content-Type: text/html
2 :
2: <html>
2 :   <head><title>Generic server</title></head>
2 :   <body>
2 :     <center>
2 :       <h2>Response from the generic server</h2>
2 :     </center>
2:    </body>
2: </html>
2: end
The following error occurred: Unable to read data from the transport connection.
[end of thread reading requests from client 2]
[end of thread reading server responses to client 2]

Lines beginning with 2: are sent from the server to client #2. The end command closes the connection from the server to the client. In our response, we have limited ourselves to the following HTTP headers:

HTTP/1.1 200 OK
2: Server: generic TCP server
2: Connection: close
2: Content-Type: text/html
2:

We do not specify the size of the file we are sending (Content-Length), but simply indicate that we will close the connection (Connection: close) after sending it. This is sufficient for the browser. Upon seeing the connection closed, it will know that the server’s response is complete and will display the HTML page that was sent to it. The page is as follows:

2: <html>
2:   <head><title>Generic server</title></head>
2 :   <body>
2 :     <center>
2 :       <h2>Generic server response</h2>
2 :     </center>
2:    </body>
2: </html>

The user then closes the connection to the client by typing the "fin" command. The browser then knows that the server's response is complete and can display it:

Image

If, in the example above, you select View/Source to see what the browser received, you get:

Image

that is, exactly what was sent from the generic server.

The code for the generic TCP server is as follows:

// packages
import java.io.*;
import java.net.*;

public class genericTCPserver{

    // main program
    public static void main(String[] args) {

    // receives the port to listen for client requests
    // creates a thread to read client requests
    // these will be displayed on the screen
    // creates a thread to read commands typed on the keyboard
    // these will be sent as a response to the client
    // the process ends with the "end" command typed on the keyboard

    final String syntax = "Syntax: pg port";
  // instance variable
         // is there an argument
     if(args.length != 1)
        error(syntax, 1);

        // the port must be an integer > 0
        int port = 0;
        boolean portError = false;
        Exception e = null;
        try{
            port = Integer.parseInt(args[0]);
        } catch (Exception e) {
            E = e;
            portError = true;
        }
        portError = portError || port <= 0;
        if(portError)
            error(syntax + "\n" + "Incorrect port (" + E + ")", 2);

     // create the listening service
    ServerSocket listener = null;
    int clients = 0;    // number of clients handled
        try{
            // Create the service
            listen = new ServerSocket(port);
            // monitoring
            System.out.println("Generic server launched on port " + port);

            // client service loop
            Socket client = null;
            while (true) { // infinite loop - will be stopped by Ctrl-C
                // waiting for a client
                client = listen.accept();

                // the service is handled by separate threads
                nbClients++;

                // create read/write threads
        new ServerSend(client, nbClients).start();
        new ReceiveServer(client, nbClients).start();

                // return to listening for requests
            }// end of while
        } catch (Exception ex) {
            // report the error
            error("The following error occurred: " + ex.getMessage(), 3);
        }//catch
    }// end main

    // display errors
    public static void error(String msg, int exitCode){
        // display error
        System.err.println(msg);
        // exit with error
        System.exit(exitCode);
    }//error
}//class

class ServerSend extends Thread{
    // class responsible for reading keyboard input
    // and sending them to a client via a TCP client passed to the constructor

    Socket client;    // the TCP client
    int clientNumber;        // client number

    // constructor
    public ServerSend(ClientSocket, int clientNumber){
        // store the TCP client
        this.client = client;
        // and its number
        this.clientNumber = clientNumber;
    }//constructor

    // thread's Run method
    public void run(){

        // local data
        PrintWriter OUT = null;        // network write stream
        String response = null;        // response read from keyboard
    BufferedReader IN = null;    // keyboard stream

        // tracking
        System.out.println("Thread for reading server responses to client " + numClient + " started");
        // error handling
        try{
            // Create network write stream
            OUT = new PrintWriter(client.getOutputStream(), true);
      // Create keyboard stream
      IN = new BufferedReader(new InputStreamReader(System.in));
            // loop for command input and transmission
            while(true){
                // client identification
                System.out.print("--> " + clientNumber + " : ");
                // read response typed on the keyboard
                response = IN.readLine().trim();
                // Done?
                if (response.toLowerCase().equals("end")) break;
                // send response to the server
                OUT.println(response);
                // next response
            }//while
        } catch (Exception ex) {
            // error
            System.err.println("The following error occurred: " + ex.getMessage());
        }//catch
        // end - closing streams
        try{
            OUT.close(); client.close();
        }catch(Exception ex){}
        // signal the end of the thread
        System.out.println("[End of thread reading server responses to client " + numClient + "]");
    }//run
}//class

class ServerReceive extends Thread{
    // Class responsible for reading the text lines sent to the server 
    // via a TCP client passed to the constructor

    Socket client;    // the TCP client
    int clientNumber;        // client number

    // constructor
    public ServerReceive(ClientSocket, int clientNumber) {
        // store the TCP client
        this.client = client;
        // and its number
        this.clientNumber = clientNumber;
    }//constructor

    // thread's Run method
    public void run(){

        // local data
        BufferedReader IN = null;        // network read stream
        String response = null;        // server response

        // monitoring
        System.out.println("Thread for reading requests from client " + numClient + " started");
        // error handling
        try{
            // Create network read stream
            IN = new BufferedReader(new InputStreamReader(client.getInputStream()));
            // Loop to read text lines from the IN stream
            while(true){
                // read network stream
                response = IN.readLine();
                // Is the stream closed?
                if(response == null) break;
                // display
                System.out.println("<-- " + response);
            }//while
        } catch (Exception ex) {
            // error
            System.err.println("The following error occurred: " + ex.getMessage());
        }//catch
        // end - closing streams
        try{
            IN.close(); client.close();
        }catch(Exception ex){}
        // signal the end of the thread
        System.out.println("[End of thread reading client requests " + numClient + "]");
    }//run
}//class