The difficulty with this form of communication is that although clients are able to execute methods on the remote object, the server object does not have a direct way to initiate a message back to the clients. In the poker server, we solve this problem by maintaining a queue of messages on the server for each client. One thread of the client is responsible for picking up these messages and processing them appropriately. These messages are all implemented as extentions of the absract ReturnMessage class. Here is the set of extentions that we are currently using:
We represent the message queue on the server side using the ArrayList class, which is a dynamically allocatable array class. There is a queue associated with each game player (see the Player class).
On the client side, once the game has started, the job of the main thread is to continuously obtain these messages as they become available (see the PokerClient class). The thread will block until a message is available; once a message is returned from the game server, it is interpreted and serviced. Because we are dealing with an object-oriented language, the different message classes implement their own unique service() methods. Thus, all the client program has to do is to call this service method without having to explicitly differentiate between the different message classes (this is done automagically for us at run time).
It is common in distributed gaming systems to employ a finite state machine model to describe both the high-level state (e.g., what phase of the game are you in?) and the transitions from one state to another. This way, when a remote method is called by a client, that method is able to know how to respond to the client's request. In this poker server, we have an explicit representation of the high-level state (as stored in the state component of the Game class).
Note that each player also has a notion of state (see the Player class).
One test that we will perform on your program is to kill both the server and one of the clients to see how the remaining processes respond. It is important that you detect and respond appropriately to these 'unfriendly' conditions. On the client side, it is easy to know that the server has disconnected, since calling a method on a remote object that no longer exists will result in a RemoteException being thrown. Detecting the disconnection of a client by the server is a bit more challenging. In the poker player, this is implemented by noting the time at which a client last picked up a message (using the GregorianCalendar class). If this time is too long (in our case, 2 seconds), then the server assumes that the client has died. In order to ensure that the clients always have a message to process, a special "ping" thread on the server side sends the PingReturnMessage to each active player at 1 second intervals. These messages do not cause the client to do anything other than to pick up another message (hence their service() method is empty).
Note that it is not necessary that a game continue when a client disconnects unexpectedly. In this case, the game could simply end with an apology to the remaining users.
java -Djava.security.policy=file:policy.all GameServerImpl
Andrew H. Fagg