soc.server.genericServer
Class Server

java.lang.Object
  extended by java.lang.Thread
      extended by soc.server.genericServer.Server
All Implemented Interfaces:
java.io.Serializable, java.lang.Cloneable, java.lang.Runnable
Direct Known Subclasses:
SOCServer

public abstract class Server
extends java.lang.Thread
implements java.io.Serializable, java.lang.Cloneable

a general purpose server.

This is the real stuff. Server subclasses won't have to care about reading/writing on the net, data consistency among threads, etc. The Server listens on either a TCP port, or for practice mode, to a LocalStringServerSocket.

Newly connecting clients arrive in run(), start a thread for the server side of their Connection or LocalStringConnection, and are integrated into server data via addConnection(StringConnection) called from that thread. If the client's connection is accepted in newConnection1(StringConnection), the per-client thread enters a while-loop and calls treat(String, StringConnection) to handle messages from the client. Treat places them in a server-wide inQueue, which is processed in a server-wide single thread called the "treater".

Alternately, it could be rejected in newConnection1 for any reason, including too many connections versus getNamedConnectionCount().

To handle inbound messages from the clients, the server-wide "treater" thread will call processCommand(String, StringConnection) for each message.

The first processed message over the connection will be from the server to the client, in newConnection1(StringConnection) or newConnection2(StringConnection). You can send out to the client there, but can't yet receive messages from it, until after newConnection2 returns. The client should ideally be named and versioned in newConnection1, but this can also be done later.

Although this generic client/server will track client versions once they are set, its basic protocol has no standardized way to inform server/client of each other's version. You must send this in an app-specific way, during the initial exchange when a client connects.

Version:
1.7
Author:
Original author: Cristian Bogdan
Lots of mods by Robert S. Thomas and Jay Budzik
Local (StringConnection) network system by Jeremy D Monin <jeremy@nand.net>
Version-tracking system, javadocs, and other minor mods by Jeremy D Monin <jeremy@nand.net>
See Also:
Serialized Form

Nested Class Summary
(package private) static class Server.Command
          Holds one message from client, for inQueue.
protected  class Server.ConnExcepDelayedPrintTask
          This object represents one client-connect or disconnect debug-print announcement within cliConnDisconPrintsPending.
private static class Server.ConnVersionCounter
          Hold info about 1 version of connected clients; for use in cliVersionsConnected.
private static class Server.ConnVersionSetCheckerTask
          Perform the periodic consistency-check of cliVersionsConnected.
protected  class Server.NetStringServerSocket
          Uses ServerSocket to implement StringServerSocket over a network.
(package private)  class Server.Treater
          Single-threaded reader of inQueue
 
Nested classes/interfaces inherited from class java.lang.Thread
java.lang.Thread.State, java.lang.Thread.UncaughtExceptionHandler
 
Field Summary
static int CLI_CONN_PRINT_TIMER_FIRE_MS
          Delay before printing a client connect/arrival announcement.
static int CLI_DISCON_PRINT_TIMER_FIRE_MS
          Delay before printing a client disconnect error announcement.
static int CLI_VERSION_SET_CONSIS_CHECK_MINUTES
          Consistency-check the cliVersionsConnected set every so often (33 minutes).
static int CLI_VERSION_SET_CONSIS_CHECK_QUICK_COUNT
          Do this many quick consistency-checks of cliVersionsConnected before doing a full check.
 java.util.HashMap<java.lang.Object,Server.ConnExcepDelayedPrintTask> cliConnDisconPrintsPending
          Client disconnect error messages, to be printed after a short delay by Server.ConnExcepDelayedPrintTask.
private  int cliVersionMax
          Minimum and maximum client version currently connected.
private  int cliVersionMin
          Minimum and maximum client version currently connected.
private  java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> cliVersionsConnected
          Versions of currently connected clients, according to StringConnection.getVersion().
private  int cliVersionsConnectedQuickCheckCount
          For cliVersionsConnected, the count of "quick" consistency-checks since the last full check.
protected  java.util.Hashtable<java.lang.Object,StringConnection> conns
          The named connections: StringConnection.getData() != null.
protected  java.lang.Exception error
           
 java.util.Vector<Server.Command> inQueue
          command messages from clients for treat(String, StringConnection)
protected  int numberCurrentConnections
          total number of current connections
protected  int numberOfConnections
          total number of connections made since startup
protected  int port
          TCP port number, or -1 for local/practice mode (LocalStringServerSocket, etc).
(package private)  StringServerSocket ss
           
protected  java.lang.String strSocketName
          LocalStringServerSocket name, or null for network mode.
protected  java.util.Vector<StringConnection> unnamedConns
          the newly connected, unnamed client connections; Adding/removing/naming/versioning of connections synchronizes on this Vector.
(package private)  boolean up
           
 java.util.Timer utilTimer
          Timer for scheduling timed/recurring tasks.
 
Fields inherited from class java.lang.Thread
MAX_PRIORITY, MIN_PRIORITY, NORM_PRIORITY
 
Constructor Summary
Server(int port)
          start listening to the given port
Server(java.lang.String stringSocketName)
          start listening to the given local string port (practice game)
 
Method Summary
 void addConnection(StringConnection c)
          Add a connection to the system.
protected  void broadcast(java.lang.String m)
          Broadcast a SOCmessage to all connected clients, named and unnamed.
protected  void broadcastToVers(java.lang.String m, int vmin, int vmax)
          Broadcast a SOCmessage to all connected clients (named and unnamed) within a certain version range.
 void clientVersionAdd(int cvers)
          Add 1 client, with this version, to cliVersionsConnected.
private  java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> clientVersionBuildMap()
          Build a fresh TreeMap of the client versions connected, to check consistency of cliVersionsConnected.
private  boolean clientVersionCheckMap(java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> tree2, boolean fullCheck)
          Perform a quick or full consistency-check of cliVersionsConnected.
private  void clientVersionRebuildMap(java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> newTree)
          Replace the current client-version map with a consistent new one, and update related fields such as minimum/maximum connected version.
 void clientVersionRem(int cvers)
          Remove 1 client, with this version, from cliVersionsConnected.
protected  StringConnection getConnection(java.lang.Object connKey)
          Given a connection's key, return the connected client.
protected  java.util.Enumeration<StringConnection> getConnections()
           
protected  int getCurrentConnectionCount()
          Get the current number of connections (both named and unnamed) to the server.
 java.lang.String getLocalSocketName()
           
 int getMaxConnectedCliVersion()
           
 int getMinConnectedCliVersion()
           
protected  int getNamedConnectionCount()
          Get the current number of named connections to the server.
 int getPort()
           
private  void initMisc()
          Minor init tasks from both constructors.
 boolean isCliVersionConnected(int cvers)
          Is a client with this version number currently connected?
 boolean isUp()
           
protected  void leaveConnection(StringConnection c)
          placeholder for doing things when a connection is closed.
 void nameConnection(StringConnection c)
          Name a current connection to the system.
protected  boolean newConnection1(StringConnection c)
          placeholder for doing things when a new connection comes, part 1 - decide whether to accept.
protected  void newConnection2(StringConnection c)
          placeholder for doing things when a new connection comes, part 2 - has been accepted and added to a connection list.
abstract  void processCommand(java.lang.String str, StringConnection con)
          Remove a queued incoming message from a client, and treat it.
 boolean processFirstCommand(java.lang.String str, StringConnection con)
          Callback to process the client's first message command specially.
 void removeConnection(StringConnection c)
          remove a connection from the system; synchronized on list of connections.
protected  void removeConnectionCleanup(StringConnection c)
          do cleanup after a remove connection
 void run()
          Run method for Server: Start a single "treater" thread for processing inbound messages, call the serverUp() callback, then wait for new connections and set them up in their own threads.
protected  void serverDown()
          placeholder for doing things when server gets down
protected  void serverUp()
          Placeholder (callback) for doing things when server comes up, after the server socket is bound and listening, in the main thread before handling any incoming connections.
 void stopServer()
          The server is being cleanly stopped, disconnect all the connections.
 void treat(java.lang.String s, StringConnection c)
          treat a request from the given connection, by adding to inQueue
 
Methods inherited from class java.lang.Thread
activeCount, checkAccess, clone, countStackFrames, currentThread, destroy, dumpStack, enumerate, getAllStackTraces, getContextClassLoader, getDefaultUncaughtExceptionHandler, getId, getName, getPriority, getStackTrace, getState, getThreadGroup, getUncaughtExceptionHandler, holdsLock, interrupt, interrupted, isAlive, isDaemon, isInterrupted, join, join, join, resume, setContextClassLoader, setDaemon, setDefaultUncaughtExceptionHandler, setName, setPriority, setUncaughtExceptionHandler, sleep, sleep, start, stop, stop, suspend, toString, yield
 
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

ss

StringServerSocket ss

up

boolean up

error

protected java.lang.Exception error

port

protected int port
TCP port number, or -1 for local/practice mode (LocalStringServerSocket, etc).


strSocketName

protected java.lang.String strSocketName
LocalStringServerSocket name, or null for network mode.


CLI_VERSION_SET_CONSIS_CHECK_MINUTES

public static final int CLI_VERSION_SET_CONSIS_CHECK_MINUTES
Consistency-check the cliVersionsConnected set every so often (33 minutes).

Since:
1.1.06
See Also:
Constant Field Values

CLI_VERSION_SET_CONSIS_CHECK_QUICK_COUNT

public static final int CLI_VERSION_SET_CONSIS_CHECK_QUICK_COUNT
Do this many quick consistency-checks of cliVersionsConnected before doing a full check.

Since:
1.1.06
See Also:
Constant Field Values

numberOfConnections

protected int numberOfConnections
total number of connections made since startup


numberCurrentConnections

protected int numberCurrentConnections
total number of current connections

Since:
1.1.06

conns

protected java.util.Hashtable<java.lang.Object,StringConnection> conns
The named connections: StringConnection.getData() != null.

See Also:
unnamedConns

unnamedConns

protected java.util.Vector<StringConnection> unnamedConns
the newly connected, unnamed client connections; Adding/removing/naming/versioning of connections synchronizes on this Vector.

See Also:
conns

inQueue

public java.util.Vector<Server.Command> inQueue
command messages from clients for treat(String, StringConnection)


cliVersionsConnected

private java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> cliVersionsConnected
Versions of currently connected clients, according to StringConnection.getVersion(). Key = Integer(version). Value = ConnVersionCounter. Synchronized on unnamedConns, like many other client-related structures.

Since:
1.1.06
See Also:
clientVersionAdd(int), clientVersionRem(int)

cliVersionMin

private int cliVersionMin
Minimum and maximum client version currently connected. Meaningless if numberOfConnections is 0.

Since:
1.1.06
See Also:
cliVersionsConnected

cliVersionMax

private int cliVersionMax
Minimum and maximum client version currently connected. Meaningless if numberOfConnections is 0.

Since:
1.1.06
See Also:
cliVersionsConnected

cliVersionsConnectedQuickCheckCount

private int cliVersionsConnectedQuickCheckCount
For cliVersionsConnected, the count of "quick" consistency-checks since the last full check.

Since:
1.1.06

utilTimer

public java.util.Timer utilTimer
Timer for scheduling timed/recurring tasks.

Since:
1.1.06

cliConnDisconPrintsPending

public java.util.HashMap<java.lang.Object,Server.ConnExcepDelayedPrintTask> cliConnDisconPrintsPending
Client disconnect error messages, to be printed after a short delay by Server.ConnExcepDelayedPrintTask. If the client reconnects during the delay, the disconnect and reconnect messages are not printed, so long as your app removes them. This is only used if D.ebugIsEnabled() is true.

Keys: The StringConnection object is used as the key within addConnection(StringConnection) (for the rejoining message). The connection keyname is used as the key within removeConnection(StringConnection) (for the leaving message); if this is null, the message is printed immediately, and not added to this map.
Values: A Server.ConnExcepDelayedPrintTask which will print the rejoining or leaving message after a delay.

After your app (which extends Server) determines that a connection is the same client that just disconnected, it should find both of the tasks in this HashMap, call TimerTask.cancel() on them, and remove them.

Since:
1.1.07
See Also:
CLI_DISCON_PRINT_TIMER_FIRE_MS

CLI_DISCON_PRINT_TIMER_FIRE_MS

public static int CLI_DISCON_PRINT_TIMER_FIRE_MS
Delay before printing a client disconnect error announcement. Should be at least 300 ms more than CLI_CONN_PRINT_TIMER_FIRE_MS, so that connects are printed before disconnects, if a connection is lost right away.

Since:
1.1.07
See Also:
cliConnDisconPrintsPending

CLI_CONN_PRINT_TIMER_FIRE_MS

public static int CLI_CONN_PRINT_TIMER_FIRE_MS
Delay before printing a client connect/arrival announcement.

Since:
1.1.07
See Also:
cliConnDisconPrintsPending
Constructor Detail

Server

public Server(int port)
start listening to the given port


Server

public Server(java.lang.String stringSocketName)
start listening to the given local string port (practice game)

Method Detail

initMisc

private void initMisc()
Minor init tasks from both constructors. Set up the recurring schedule of cliVersionsConnected here.


getConnection

protected StringConnection getConnection(java.lang.Object connKey)
Given a connection's key, return the connected client.

Parameters:
connKey - Object key data, as in StringConnection.getData(); if null, returns null
Returns:
The connection with this key, or null if none

getConnections

protected java.util.Enumeration<StringConnection> getConnections()
Returns:
the list of named connections: StringConnections where StringConnection.getData() is not null

getPort

public int getPort()
Returns:
the TCP port number we're listening on, if any, or null if using local string ports instead.
Since:
1.1.12
See Also:
getLocalSocketName()

getLocalSocketName

public java.lang.String getLocalSocketName()
Returns:
the local socket name we're listening on, or null if using TCP instead.
Since:
1.1.12
See Also:
getPort()

getNamedConnectionCount

protected final int getNamedConnectionCount()
Get the current number of named connections to the server.

Returns:
the count of named connections: StringConnections where StringConnection.getData() is not null
See Also:
getCurrentConnectionCount()

getCurrentConnectionCount

protected int getCurrentConnectionCount()
Get the current number of connections (both named and unnamed) to the server.

Returns:
the count of connections, both unnamed and named (StringConnection.getData() not null).
Since:
1.1.13
See Also:
getNamedConnectionCount()

isUp

public boolean isUp()

run

public void run()
Run method for Server: Start a single "treater" thread for processing inbound messages, call the serverUp() callback, then wait for new connections and set them up in their own threads.

Specified by:
run in interface java.lang.Runnable
Overrides:
run in class java.lang.Thread

treat

public void treat(java.lang.String s,
                  StringConnection c)
treat a request from the given connection, by adding to inQueue


processCommand

public abstract void processCommand(java.lang.String str,
                                    StringConnection con)
Remove a queued incoming message from a client, and treat it. Called from the single 'treater' thread. Do not block or sleep because this is single-threaded.

Parameters:
str - Contents of message from the client
con - Connection (client) sending this message

processFirstCommand

public boolean processFirstCommand(java.lang.String str,
                                   StringConnection con)
Callback to process the client's first message command specially. This default implementation does nothing and returns false; override it in your app if needed.

Parameters:
str - Contents of first message from the client
con - Connection (client) sending this message
Returns:
true if processed here, false if this message should be queued up and processed by the normal processCommand(String, StringConnection).

serverUp

protected void serverUp()
Placeholder (callback) for doing things when server comes up, after the server socket is bound and listening, in the main thread before handling any incoming connections.

Once this method completes, server begins its main loop of listening for incoming client connections, and starting a Thread for each one to handle that client's messages.

Since:
1.1.09

serverDown

protected void serverDown()
placeholder for doing things when server gets down


newConnection1

protected boolean newConnection1(StringConnection c)
placeholder for doing things when a new connection comes, part 1 - decide whether to accept. Unless you override this method, always returns true. This is called within addConnection(StringConnection).

If the connection is accepted, it's added to a list (unnamedConns or conns), and also added to the version collection.

This method is called within a per-client thread. You can send to client, but can't yet receive messages from them.

Should send a message to the client in either newConnection1(StringConnection) or newConnection2(StringConnection). You may also name the connection here by calling c.setData, which will help add to conns or unnamedConns. This is also where the version should be set.

Note that addConnection(StringConnection) won't close the channel or take other action to disconnect a rejected client.

SYNCHRONIZATION NOTE: During the call to newConnection1, the monitor lock of unnamedConns is held. Thus, defer as much as possible until newConnection2(StringConnection) (after the connection is accepted).

Parameters:
c - incoming connection to evaluate and act on
Returns:
true to accept and continue, false if you have rejected this connection; if false, addConnection will call StringConnection.disconnectSoft().
See Also:
addConnection(StringConnection), newConnection2(StringConnection), nameConnection(StringConnection)

newConnection2

protected void newConnection2(StringConnection c)
placeholder for doing things when a new connection comes, part 2 - has been accepted and added to a connection list. Unlike newConnection1(StringConnection), no connection-list locks are held when this method is called. This is called within addConnection(StringConnection).

This method is called within a per-client thread. You can send to client, but can't yet receive messages from them.


leaveConnection

protected void leaveConnection(StringConnection c)
placeholder for doing things when a connection is closed. Called after connection is removed from conns collection and version collection, and after c.disconnect() has been called.

This method is called within a per-client thread.


stopServer

public void stopServer()
The server is being cleanly stopped, disconnect all the connections. Calls serverDown() before disconnect; if your child class has more work to do (such as sending a final message to all clients, or disconnecting from a database), override serverDown() or stopServer(). Check isUp() before calling.


removeConnection

public void removeConnection(StringConnection c)
remove a connection from the system; synchronized on list of connections. The callback leaveConnection(StringConnection) will be called, after calling StringConnection.disconnect() on c.

This method is called within a per-client thread. The add to cliConnDisconPrintsPending is unsynchronized.

Parameters:
c - Connection to remove; will call its disconnect() method and remove it from the server state.

removeConnectionCleanup

protected void removeConnectionCleanup(StringConnection c)
do cleanup after a remove connection


addConnection

public void addConnection(StringConnection c)
Add a connection to the system. Called within a per-client thread. StringConnection.connect() is called at the start of this method.

App-specific work should be done by overriding newConnection1(StringConnection) and newConnection2(StringConnection). The connection naming and version is checked here (after newConnection1).

Locking: Synchronized on unnamedConns, although named conns (getData not null) are added to conns, not unnamedConns. The add to cliConnDisconPrintsPending is unsynchronized.

Parameters:
c - Connecting client; its key data (StringConnection.getData()) must not be null.
See Also:
nameConnection(StringConnection), removeConnection(StringConnection)

nameConnection

public void nameConnection(StringConnection c)
                    throws java.lang.IllegalArgumentException
Name a current connection to the system. Call c.setData(name) just before calling this method. Can be called once per connection (once named, cannot be changed). Synchronized on unnamedConns.

If you name the connection inside newConnection1(StringConnection), you don't need to call nameConnection, because it hasn't yet been added to a connection list.

Parameters:
c - Connected client; its key data (StringConnection.getData()) must not be null
Throws:
java.lang.IllegalArgumentException - If c isn't already connected, if c.getData() returns null, or if nameConnection has previously been called for this connection.
See Also:
addConnection(StringConnection)

clientVersionAdd

public void clientVersionAdd(int cvers)
Add 1 client, with this version, to cliVersionsConnected.

Locks: Caller should synchronize on unnamedConns, and call just before incrementing numberCurrentConnections.

Parameters:
cvers - Client version number, from StringConnection.getVersion().
Since:
1.1.06
See Also:
clientVersionRem(int), getMinConnectedCliVersion(), getMaxConnectedCliVersion()

clientVersionRem

public void clientVersionRem(int cvers)
Remove 1 client, with this version, from cliVersionsConnected.

Locks: Caller should synchronize on unnamedConns, right after decrementing numberCurrentConnections (in case a consistency-check is called from here).

Parameters:
cvers - Client version number, from StringConnection.getVersion().
Since:
1.1.06
See Also:
clientVersionAdd(int), getMinConnectedCliVersion(), getMaxConnectedCliVersion()

getMinConnectedCliVersion

public int getMinConnectedCliVersion()
Returns:
the version number of the oldest-version client that is currently connected
Since:
1.1.06
See Also:
isCliVersionConnected(int)

getMaxConnectedCliVersion

public int getMaxConnectedCliVersion()
Returns:
the version number of the newest-version client that is currently connected
Since:
1.1.06
See Also:
isCliVersionConnected(int)

isCliVersionConnected

public boolean isCliVersionConnected(int cvers)
Is a client with this version number currently connected?

Parameters:
cvers - Client version number, from StringConnection.getVersion().
Returns:
True if a client of this version is currently connected, according to calls to clientVersionAdd(int) and clientVersionRem(int)
Since:
1.1.13
See Also:
getMinConnectedCliVersion(), getMaxConnectedCliVersion()

clientVersionBuildMap

private java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> clientVersionBuildMap()
Build a fresh TreeMap of the client versions connected, to check consistency of cliVersionsConnected.

Locks: Caller should synchronize on unnamedConns.

Since:
1.1.06
See Also:
clientVersionCheckMap(TreeMap, boolean), clientVersionRebuildMap(TreeMap)

clientVersionCheckMap

private boolean clientVersionCheckMap(java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> tree2,
                                      boolean fullCheck)
Perform a quick or full consistency-check of cliVersionsConnected. Quick check: Check the number of connected clients, versus the number in cliVersionsConnected.
Full check: Build a second tree, compare it to the current tree cliVersionsConnected.

Locks: Caller should synchronize on unnamedConns.

Parameters:
tree2 - A tree to check, or null to generate a new one here by calling clientVersionBuildMap(). Not used in the quick check.
fullCheck - True for the full check, false for the quick check.
Returns:
True if consistent, false if any problems were found.
Since:
1.1.06
See Also:
clientVersionRebuildMap(TreeMap)

clientVersionRebuildMap

private void clientVersionRebuildMap(java.util.TreeMap<java.lang.Integer,Server.ConnVersionCounter> newTree)
Replace the current client-version map with a consistent new one, and update related fields such as minimum/maximum connected version.

Locks: Caller should synchronize on unnamedConns.

Parameters:
newTree - Newly built version treemap as generated by clientVersionBuildMap(), or null to generate here.
Since:
1.1.06
See Also:
clientVersionCheckMap(TreeMap, boolean)

broadcast

protected void broadcast(java.lang.String m)
Broadcast a SOCmessage to all connected clients, named and unnamed.

Parameters:
m - SOCmessage string, generated by SOCMessage.toCmd()
See Also:
broadcastToVers(String, int, int)

broadcastToVers

protected void broadcastToVers(java.lang.String m,
                               int vmin,
                               int vmax)
Broadcast a SOCmessage to all connected clients (named and unnamed) within a certain version range.

The range is inclusive: Clients of version vmin and newer, up to and including vmax, receive the broadcast. If vmin > vmax, do nothing.

Parameters:
m - SOCmessage string, generated by SOCMessage.toCmd()
vmin - Minimum version, as returned by StringConnection.getVersion(), or Integer.MIN_VALUE
vmax - Maximum version, or Integer.MAX_VALUE
Since:
1.1.06
See Also:
broadcast(String)