Writing a Multithreaded Socket Server

This example demonstrates the creation of a multithreaded socket server application. The server is currently a simple Echo Server, which simply accepts what the client says and sends it back. The example demonstrates the basic infrastructure for a true multithreaded server application and can be directly enhanced to perform as any server application.

The EchoServer accepts multiple client connections and is capable of serving them simultaneously. It uses a new thread to service every new client, which allows it to accepts new clients even before all clients are serviced. The server also identifies each client connection with a unique ID, making it easy to identify clients if required.

The EchoServer only understands one command, “quit”. On receiving this command, it quits the thread servicing the client and closes the connections. Also worth noting are the various places where a network connection could fail. All conditions are handled gracefully by EchoServer, thus making sure that the server never leaves behind residual threads and that the client is not capable of crashing the server.

EchoServer.java : The Server application 

import java.net.*;
import java.io.*;

public class EchoServer
{ 
    ServerSocket m_ServerSocket;


    public EchoServer()
    {
        try
        {
            // Create the server socket. 
            m_ServerSocket = new ServerSocket(12111);
        }
        catch(IOException ioe)
        {
            System.out.println("Could not create server socket at 12111. Quitting.");
            System.exit(-1);
        }

        System.out.println("Listening for clients on 12111...");

        // Successfully created Server Socket. Now wait for connections. 
        int id = 0;
        while(true)
        {
            try
            {
                // Accept incoming connections. 
                Socket clientSocket = m_ServerSocket.accept();

                // accept() will block until a client connects to the server. 
                // If execution reaches this point, then it means that a client 
                // socket has been accepted. 

                // For each client, we will start a service thread to 
                // service the client requests. This is to demonstrate a 
                // multithreaded server, although not required for such a 
                // trivial application. Starting a thread also lets our 
                // EchoServer accept multiple connections simultaneously. 

                // Start a service thread 

                ClientServiceThread cliThread = new ClientServiceThread(clientSocket, id++);
                cliThread.start();
            }
            catch(IOException ioe)
            {
                System.out.println("Exception encountered on accept. Ignoring. Stack Trace :");
                ioe.printStackTrace();
            }
        }
    }

    public static void main (String[] args)
    {
        new EchoServer();
    }


    class ClientServiceThread extends Thread
    {
        Socket m_clientSocket;
        int m_clientID = -1;
        boolean m_bRunThread = true;

        ClientServiceThread(Socket s, int clientID)
        {
            m_clientSocket = s;
            m_clientID = clientID;
        }

        public void run()
        {
            // Obtain the input stream and the output stream for the socket 
            // A good practice is to encapsulate them with a BufferedReader 
            // and a PrintWriter as shown below. 
            BufferedReader in = null;
            PrintWriter out = null;

            // Print out details of this connection 
            System.out.println("Accepted Client : ID - " + m_clientID + " : Address - " +
                    m_clientSocket.getInetAddress().getHostName());

            try
            {
                in = new BufferedReader(new InputStreamReader(m_clientSocket.getInputStream()));
                out = new PrintWriter(new OutputStreamWriter(m_clientSocket.getOutputStream()));

                // At this point, we can read for input and reply with appropriate output. 

                // Run in a loop until m_bRunThread is set to false 
                while(m_bRunThread)
                {
                    // read incoming stream 
                    String clientCommand = in.readLine();

                    System.out.println("Client Says :" + clientCommand);


                    if(clientCommand.equalsIgnoreCase("quit"))
                    {
                        // Special command. Quit this thread 
                        m_bRunThread = false;
                        System.out.print("Stopping client thread for client : " + m_clientID);
                    }
                    else
                    {
                        // Echo it back to the client. 
                        out.println(clientCommand);
                        out.flush();
                    }
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                // Clean up 
                try
                {
                    in.close();
                    out.close();
                    m_clientSocket.close();
                    System.out.println("...Stopped");
                }
                catch(IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }
        }
    }
}

EchoClient.java : The Client application 

import java.net.*; 
import java.io.*; 

// A client for our multithreaded EchoServer. 
public class EchoClient 
{ 
    public static void main(String[] args) 
    { 
        // First parameter has to be machine name 
        if(args.length == 0) 
        { 
            System.out.println("Usage : EchoClient <serverName>"); 
            return; 
        } 
         
        Socket s = null; 
         
        // Create the socket connection to the EchoServer. 
        try 
        { 
            s = new Socket(args[0], 12111); 
        }         
        catch(UnknownHostException uhe) 
        { 
            // Host unreachable 
            System.out.println("Unknown Host :" + args[0]); 
            s = null; 
        } 
        catch(IOException ioe) 
        { 
            // Cannot connect to port on given host 
            System.out.println("Cant connect to server at 12111. Make sure it is running."); 
            s = null; 
        } 
         
        if(s == null) 
            System.exit(-1); 
         
        BufferedReader in = null; 
        PrintWriter out = null; 
         
        try 
        { 
            // Create the streams to send and receive information 
            in = new BufferedReader(new InputStreamReader(s.getInputStream())); 
            out = new PrintWriter(new OutputStreamWriter(s.getOutputStream())); 
             
            // Since this is the client, we will initiate the talking. 
            // Send a string. 
            out.println("Hello"); 
            out.flush(); 
            // receive the reply. 
            System.out.println("Server Says : " + in.readLine()); 
             
            // Send a string. 
            out.println("This"); 
            out.flush(); 
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine()); 
             
            // Send a string. 
            out.println("is"); 
            out.flush(); 
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine()); 
             
            // Send a string. 
            out.println("a"); 
            out.flush(); 
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine()); 

            // Send a string. 
            out.println("Test"); 
            out.flush(); 
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine()); 
             
            // Send the special string to tell server to quit. 
            out.println("Quit"); 
            out.flush(); 
        } 
        catch(IOException ioe) 
        { 
            System.out.println("Exception during communication. Server probably closed connection."); 
        } 
        finally 
        { 
            try 
            { 
                // Close the streams 
                out.close(); 
                in.close(); 
                // Close the socket before quitting 
                s.close(); 
            } 
            catch(Exception e) 
            { 
                e.printStackTrace(); 
            }                 
        }         
    } 
} 

Demonstration

To run the server, compile the source and run it by typing “java EchoServer” on the command prompt.

>java EchoServer
Listening for clients on 12111...

The server indicates that it is listening for connections. To run the client, simply run java EchoClient localhost (if running on the same machine), substitute “localhost” with the machine name the server is running on, if over a network.

>java EchoClient localhost
Server Says : Hello
Server Says : This
Server Says : is
Server Says : a
Server Says : Test

On running the client, the above output will be displayed at the client side, and the following output, on the server side.

Accepted Client : ID - 0 : Address - 127.0.0.1
Client Says :Hello
Client Says :This
Client Says :is
Client Says :a
Client Says :Test
Client Says :Quit
Stopping client thread for client : 0...Stopped
0

Leave a Reply