Package com.stephengware.java.mss

The Multi-Socket Server or mss package provides an easy way to manage asynchronous input from many different sockets by placing all network events into a queue so that they can be handled one at a time on a single thread.

See: Description

Package com.stephengware.java.mss Description

The Multi-Socket Server or mss package provides an easy way to manage asynchronous input from many different sockets by placing all network events into a queue so that they can be handled one at a time on a single thread.

Most servers have at least three kinds of threads:

  1. One main thread which does the actual processing.
  2. One thread to listen for and accept new sockets.
  3. One thread per socket to listen for input from that socket.

With so many threads using the same data structures, it can be difficult to avoid race conditions and the other kinds of bugs that crop up in multi-threaded applications, not to mention the difficulty in ensuring that all threads start and end properly. This package provides one solution to that problem.

The main class in this package is EventQueue, which encapsulates the new socket listener thread (#2 above) and the per-socket listener threads (#3 above). Once the queue is started, it begins to fill with events that represent important network activity. This means you only have to worry about programming the main thread (#1), which can pop events off of the queue one by one and handle them without worrying about synchronization.

There are six kinds of events that will appear in the queue:

This package also provides a convenient abstract class, called MultiSocketServer which can be extended to quickly and easily create a multi-threaded server application. When you extend MultiSocketServer, you must define six methods, each of which correspond to the six events above:

These methods will be called one at time as their corresponding events occur, but they will never be called at the same time. In other words, if the server receives a new event while one of the onX() methods is executing, it will wait for that method to return before calling the next onX() method. This means you don't have to worry about synchronizing the data structures used in your server (unless they will be used by other threads).

Here is a simple example to demonstrate how easy it is to create a chat room server using MultiSocketServer:

 
 import com.stephengware.java.mss.*;
 import java.io.*;
 import java.util.HashMap;
 
 public class ChatServer extends MultiSocketServer {
 
      public static final int DEFAULT_PORT = 5555;
      protected final HashMap<Connection, String> names = new HashMap<>();
      protected final Writer log;
      
      public static void main(String[] args){
           int port = DEFAULT_PORT;
           if(args.length > 0){
                try{
                     port = Integer.parseInt(args[0]);
                }
                catch(NumberFormatException ex){
                     // do nothing
                }
           }
           ChatServer server = new ChatServer(port, new PrintWriter(System.out));
           server.start();
      }
      
      public ChatServer(int port, Writer log){
           super(port);
           this.log = log;
      }
      
      @Override
      protected void onStart(){
           log("Chat server has started.");
      }
      
      @Override
      protected void onConnect(Connection connection){
           log("A new client has connected.");
           connection.write("Welcome. Please enter your name.");
      }
      
      @Override
      protected void onInput(Connection connection, String input){
           // If the person already has a name, this input is a chat message.
           if(names.containsKey(connection)){
                String message = names.get(connection) + ": " + input;
                log(message);
                writeToAll(message);
           }
           // If the person does not yet have a name, this input is their name.
           else{
                log("Identified client \"" + input + "\".");
                writeToAll(input + " has joined.");
                names.put(connection, input);
           }
      }
      
      @Override
      protected void onDisconnect(Connection connection){
           String name = names.get(connection);
           // If they never gave a name, do nothing.
           if(name == null)
                return;
           // If they did give a name, tell the room they have left.
           log("Client \"" + name + "\" has disconnected.");
           names.remove(connection);
           writeToAll(name + " has left.");
      }
      
      @Override
      protected void onException(Exception exception){
           exception.printStackTrace();
      }
      
      @Override
      protected void onStop(){
           log("Chat server has stopped.");
      }
      
      protected void log(String message){
           try{
                log.append(message);
                log.append("\n");
                log.flush();
           }
           catch(IOException ex){
                ex.printStackTrace();
                stop();
           }
      }
      
      protected void writeToAll(String message){
           for(Connection connection : names.keySet())
                connection.write(message);
      }
 }
 
 
Version:
1
Author:
Stephen G. Ware