A Threaded Server

Obviously, a server that can handle only one connection at a time on a port isn't very useful for most applications, including a Talker. The solution in Java is to put each new connection in its own thread. This thread will then implement the connection processing.

Note that each class implements the Runnable interface. That way each object will be in its own thread.

Server.java

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

public class Server implements Runnable
{
    // listening flag
    public boolean bListening;

    // server thread reference
    private Thread tListener = null;

    public Server()
    {
        try
        {
            // create a new server socket
            ServerSocket server = new ServerSocket(5555);

            // start the listening thread
            start()
        }
        catch (IOException e)
        {
            System.err.println("Couldn't create server socket: "+e.toString());
        }
    }

    public void start()
    {
        if (tListener == null)
        {
            tListener = new Thread(this);
        } 
        tListener.start();
    }
    
    // This thread is designed to conform to the 1.2 threading
    // mechanism (where stop() is deprecated).    
    public void run()
    {
        // set the listening flag
        bListening = true;

        // Socket that represents the current connection
        Socket connection;

        // loop while listening
        while (bListening)
        {
            // accept a connection on the server socket and
            // pass it to the current connection variable
            // This will block until a connection is made.
            try
            {
                connection = server.accept();

                // create a new handler for this connection
                new Handler(connection);

                // we're done...null out the reference
                connection = null;
            }
            catch (IOException e)
            {
                System.err.println("Error accepting a connection: "+e.toString());
            }
        }
    }
}

Handler.java

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

public abstract class Handler implements Runnable
{
    // Socket for communication with the client
    private Socket connection;

    // Buffered readers and writers to communicate
    // with the user over the socket
    protected BufferedReader in;
    protected BufferedWriter out;

    // Thread for listening for input on the connection
    private Thread tListener;

    // Listening flag
    public boolean bListening;

    // constructor
    public Handler(Socket connection)
    {
        this.connection = connection;

        try
        {
            // get the I/O streams and turn them into readers
            in = new BufferedReader(new BufferedInputStream(connection.getInputStream()));
            out = new BufferedWriter(new BufferedOutputStream(connection.getOutputStream()));

            // start the listening thread
            start();
        } 
        catch (IOException e)
        {
            System.err.println("Unable to get I/O streams off the connection: "+e.toString());
        }
    }

    // Start the listening thread
    public void start()
    {
        if (tListener == null)
        {
            tListener = new Thread(this);
        }

        tListener.start();
    }

    // Get input from the client and pass it off to a processor
    public void run()
    {
        // set the listening flag
        bListening = true;

        // the current line of input
        String strLine;

        while (bListening)
        {
            try
            {
                // block until a line is received
                strLine = in.readLine();

                // do something with the line
                processLine(strLine);

                // null out input just in case the garbage collector
                // is aggressive, or we had a really long String
                strLine = null;
            }
            catch (IOException e)
            {
                System.err.println("Error reading input from client: "+e.toString());
            }
        }
 
        // done processing? close the socket
        try
        {
            connection.close();
        }
        catch (IOException e)
        {
        }

        // null out variables so the garbage collector has a chance
        in = null;
        out = null;
        connection = null;
    }

    // Write a string to the client
    public void write(String strLine)
    {
        try
        {
            // write the string to the client buffer
            out.write(strLine, strLine.length());

            // flush the buffer to the client, even if not full
            out.flush();
        }
        catch (IOException e)
        {
            System.err.println("Error writing to client: "+e.toString());
        }
    }

    // you need to implement this...
    public abstract void processLine(String strLine);
}

Server

The Server class is the basic model for all future servers that we will be discussion. In essence:
  1. It creates the ServerSocket
  2. Sets up and starts the listening thread
  3. Blocks, waiting for a new connection
  4. Hands that new connection off to a Handler class
  5. Loops back to blocking, waiting for a new connection

It is essential that you understand this code to appreciate what we will change in it--such as variable ports and dynamic class creation. The next version of the server will be generic enough to serve almost any needs.

Feel free to quibble about the efficiency of the code inside the Server.run() method with the assignment of the socket connection to a variable. This was written this way for clarity, not to demonstrate bummed code.

Handler

You should notice that some of the code is similar from the Basic Server. How it is implemented, and why it is broken out of the Server class is what is very important. Also, you should note that this class is declared Abstract--this will get a clean compile, as well as encourage extending this class to provide real functionality.

As far as creating a Talker goes, this class will changed in a few, key ways. Most future effort will be spent extending this class to create a protocol specific handler, but some elements will move up to the next level of Handler, rather than remain here (such as the while loop control).

This class, in essence:

  1. Keep track of the socket (for management)
  2. Create buffered readers and writers for line oriented I/O
  3. Set up and start a listening thread (for input from the client)
  4. Loop and listen for input
  5. Process the input line-by-line
  6. Accept Strings that will be sent as output to the client