See: Description
Class | Description |
---|---|
ConnectEvent |
Indicates that the server has accepted a new socket.
|
Connection |
Represents an individual connection to the server.
|
DisconnectEvent |
Indicates that a connection has disconnected from the server.
|
Event |
The parent class of every kind of event that can be returned by an
EventQueue . |
EventQueue |
An EventQueue listens for incoming
Connection s and
allows the user to get a sequence of Event s via the
pop method. |
ExceptionEvent |
Indicates that an exception has occurred while creating a new connection,
processing the input from a connection, or closing a connection.>
|
InputEvent |
Indicates that a connection has sent a message to the server.
|
MultiSocketServer |
A convenient abstract class which maintains its own
EventQueue
and calls the relevant onX() method each time a new event is
returned from the queue. |
Enum | Description |
---|---|
Event.Type |
The various types of events.
|
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:
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:
Event.SERVER_START_EVENT
occurs
once when the server starts.ConnectEvent
occurs each time new
socket connects to the server.InputEvent
occurs each time one of
the connected sockets sends a message to the server.DisconnectEvent
occurs each time
a socket disconnects.Event.SERVER_START_EVENT
occurs
once when the server stops.ExceptionEvent
occurs any time an
uncaught or IOException occurs.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:
onStart()
is called
when the server starts.onConnect(Connection)
is called each time a new socket connects to the server.onInput(Connection, String)
is called each time a socket receives input.onDisconnect(Connection)
is called each time one of the connected sockets disconnects.onStop()
is called
when the server stops.onException(Exception)
is called any time an uncaught exception or IOException occurs.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);
}
}