The Client/Server Communication Component (CSCC) is a very simple, but powerful library, entirely written in Java. It can be used for transmitting messages containing arbitrary data asynchronously between several clients and/or a server. In contrast to Remote Method Invocation, the CSCC should be easy to use and a lightweight alternative for communication.

Figure 1: Several clients might communicate with and over a central server.

The server can provide a service for the client, e.g. it may strore centralized data. The clients are able to communicate with the central server or other registered clients (see Fig. 1). In a non-synchronized system, asynchronous communication can cause concurrency problems. The CSCC provides an automatic synchronization mechanism for handling this problem. The mechanism is transparent for the user of the CSCC and therefore the component is easy to use. We distinguish between two different type of communication events that can occur at the clients or the server, respectively. The first one is the so-called data event which is delivered as soon as a data package is received at the target. It is representedby an object of the class {@link ch.unizh.ifi.comm.DataEvent}. The second event type occurs if one or more nodes participating the communication are informed about the state of the coommunication, e.g. if a client loses the connection or a new client joins communication.  The server has to implement a logic interpreting the events. Events might be processed by the server or they might be sent directly to one or more clients.


Functionality

The following functionality is provided by the CSCC:

The following classes and interfaces provide the functionality of the C/S communication component:

Additional information is provided by these packages. The following UML class diagramm shows the public classes of the package {@link ch.unizh.ifi.comm}:

Figure 2: A class diagramm of all classes that build the interface of the CSCC.


Receiving Data

In General

Receiving data events can be handled in the same way by the client, as well the server implementation. In the CSCC, there are two options for receiving data events:

 

Server Side Identification of the Sending Client

As soon as a data event from a client is received by the server, the server assigns automatically the client identification object to the particular data event. The current implementation uses a {@link java.lang.Integer} object containing a communication system wide unique number as client identification object. This object can be used to  identify the client and to relate messages to the client. 


 

Sending Data 

Client Side Sending

Sending data from the one client participant of the system to a target participant is an easy task. After instantiating a {@link ch.unizh.ifi.comm.ClientConnectionManager} object at the client and opening a connection to a server (by using the method {@link ch.unizh.ifi.comm.ClientConnectionManager#open(String name)}), data can be sent to the target by using the method {@link ch.unizh.ifi.comm.ClientConnectionManager#sendDataEvent(DataEvent de)}. The given {@link ch.unizh.ifi.comm.DataEvent} contains the data which is asynchronously transmitted to the target.

Server Side Sending

There are two methods to send {@link ch.unizh.ifi.comm.DataEvent} objects to the client. The first method is used to broadcast an event object to all connected clients. This is done by using the {@link ch.unizh.ifi.comm.ServerConnectionManager#sendBroadCastEvent(DataEvent de)} method. The method {@link ch.unizh.ifi.comm.ServerConnectionManager#sendDataEvent(DataEvent de, Object clientId)} sends the message to client, which is identified by the parameter objectId. objectID is an identification object provided automatically by the server  (see also Communication Events).


Communication Events

A communication event occurs every time if the state of a connection between two communication partners changes. A communication event is expressed in the CSCC by the class {@link ch.unizh.ifi.comm.CommunicationEvent}. Five different types of events can occur which are described by the following constants:

This event type can be queried by calling the method {@link ch.unizh.ifi.comm.CommunicationEvent#getEventId()}. For receiving communication events, an object of the type {@link ch.unizh.ifi.comm.CommunicationEventObserver} has to be added to the communication observer list. This is done by calling the method {@link ch.unizh.ifi.comm.ServerCommunicationManager#addObserver(ch.unizh.ifi.comm.CommunicationEventObserver o)} for the server side communication observer, respectively {@link ch.unizh.ifi.comm.ClientCommunicationManager#addObserver(ch.unizh.ifi.comm.CommunicationEventObserver o)} for the client side communication observer.

Under some circumstances, it is necessary to identify the connection of the client which caused the event by other participants of the communication. Therefore the method {@link ch.unizh.ifi.comm.CommunicationEvent#getData()} returns an identifier object. This object is used to identify the client uniquely. In this implementation this object is a {@link java.lang.Integer} object containing a unique integer number. The events occuring at the server which contain client ids are: {@link ch.unizh.ifi.comm.CommunicationEvent#CONNECTION_ESTABLISHED}, {@link ch.unizh.ifi.comm.CommunicationEvent#CONNECTION_BROKEN} and  {@link ch.unizh.ifi.comm.CommunicationEvent# CONNECTION_CLOSED}.


Code Example

The following sections contain a short tutorial describing the usage of the library in push mode:

Server Side

For creating a server, follow these steps:

  1. Instantiate a new {@link ch.unizh.ifi.comm.ServerConnectionManager} object. An object of this class handles the communication from a server:

ServerConnectionManager scm = new ServerConnectionManager(2510);

  1. This statement instantiates a new {@link ch.unizh.ifi.comm.ServerConnectionManager}object which uses the TCP/IP port 2510 listening for client connections.
  2. Then start the listening for new client connections by calling

    scm.beginListening();

     

  3. Then the server begins to listen for new client network connections.
  4. To receive data events from the clients, you have to define a class which is responsible for receiving data events from the clients implements the interface {@link ch.unizh.ifi.comm.DataEventReceivingObserver}, in the class which receives the data events:

    public class MyServerDataEventReceiver implements DataEventReceivingObserver {
        ...

        public void receiveEvent(DataEvent de) { 
            // here follows some code, handling the receiving of the DataEvent objects
        }
    }
  5. To receive communication events (i.e. notifications about changes in the state of the communication), you have to implement the interface {@link ch.unizh.ifi.comm.CommunicationEventObserver}:

    public class MyServerCommunicationEventReceiver implements CommunicationEventObserver {
        ...

        public void void receiveEvent(CommunicationEvent ce) { 
            // here follows some code, handling the receiving of the CommunicationEvent objects
        }
    }

Client

For creating a client, follow these steps:

  1. Instantiate a new {@link ch.unizh.ifi.comm.ClientConnectionManager} object. An object of this class handles the communication between a client and a server:

ClientConnectionManager ccm = new ClientConnectionManager(2510);

  1. This statement instantiates a new {@link ch.unizh.ifi.comm.ClientConnectionManager}object which uses the TCP/IP port 2510 for connections to the server.
  2. A new connection to the server is opened by:

    ccm.open("127.0.0.1");

  3. The above statement opens a connection to the localhost machine, i.e. it is supposed that the server is running on the same machine (in this example on port 2510). A connection to remote machines is done by specifying an IP address or DNS name varying from the above one. 
  4. To receive data events from the server, you should define a class which is responsible for receiving data events. For this reason, implement the interface {@link ch.unizh.ifi.comm.DataEventReceivingObserver} in the class which should receive the data events:

    public class MyClientDataEventReceiver implements DataEventReceivingObserver {
        ...

        public void receiveEvent(DataEvent de) { 
            // here follows some code, hanlding the receiving of the DataEvent objects
        }
    }

  5. To receive communication events (changes of the communication state), you have to implement the interface {@link ch.unizh.ifi.comm.CommunicationEventObserver}:

    public class MyClientCommunicationEventReceiver implements CommunicationEventObserver {
        ...

        public void void receiveEvent(CommunicationEvent ce) { 
            // here follows some code, handling the receiving of the CommunicationEvent objects
        }
    }

 

Full Example

A full-fledged example of a client/server application can look like the following:

/* Server */
import ch.unizh.ifi.comm.ServerConnectionManager;
import ch.unizh.ifi.comm.DataEventReceivingObserver;
import ch.unizh.ifi.comm.DataEvent;
import ch.unizh.ifi.comm.CommunicationEventObserver;
import ch.unizh.ifi.comm.CommunicationEvent;

// The server incorporates also the data observer and the communication
// observer interface
public class TestServer implements DataEventReceivingObserver, CommunicationEventObserver {

    // provides the server connection
    ServerConnectionManager scm; 

    public TestServer() {
        scm = new ServerConnectionManager(2510); 
        // add comm. observer
        scm.addObserver((CommunicationEventObserver)this); 
        // add data event observer
        scm.addObserver((DataEventReceivingObserver)this); 
        // start listening for new connections
        scm.beginListening();
   
  }

    public void receiveEvent(DataEvent de) {
        // data event from the client, just print out the content
        System.out.println("Client says " + de.getData().toString()); 
        scm.sendBroadCastEvent(new DataEvent(new Integer(1),"Pong"));
    }

    // receives all the communication events
    public void receiveEvent(CommunicationEvent ce) {
        String data = "";
        // if the communication event contains data
        if (ce.getData() == null) { // identifying the client
            System.out.println("No additional data about comminication event"); 
        } else {
            // there is a description about the client --> client number.
            System.out.println("Number of client: " + ce.getData()); 
            data = ce.getData().toString();
        }

        // ask for all possible communication events.
   
          // print the event which occurred
        switch (ce.getEventId()) {
            case (CommunicationEvent.CONNECTION_ESTABLISHED):
                System.out.println("connection opened!");
                break;
            case (CommunicationEvent.CONNECTION_BROKEN):
                System.out.println("connection broken");
                break;
            case (CommunicationEvent.CONNECTION_CLOSED):
                System.out.println("connection closed");
                break; 
            case (CommunicationEvent.CONNECTION_START_LISTENING):
                System.out.println("start listening");
                break; 
            case (CommunicationEvent.CONNECTION_STOP_LISTENING):
                System.out.println("stop listening");
                break; 
        } 
    }

    // starts up the server
    public static void main(String[] args) {
        TestServer t = new TestServer();
    }
}

/* Client */
import ch.unizh.ifi.comm.ClientConnectionManager;
import ch.unizh.ifi.comm.DataEvent;
import ch.unizh.ifi.comm.DataEventReceivingObserver;
import ch.unizh.ifi.comm.CommunicationEvent;
import ch.unizh.ifi.comm.CommunicationEventObserver;

// The server incorporates also the data observer interface and the communication
// observer interface
public class TestClient implements DataEventReceivingObserver, CommunicationEventObserver {

    // client connection manager
    ClientConnectionManager ccm; 

    TestClient() {
        // client connection manager, preparing for the connection to the server
        ccm = new ClientConnectionManager(2510); 

        // add as comm. event observer
        ccm.addObserver((CommunicationEventObserver)this); 

        // add as data event observer
        ccm.addObserver((DataEventReceivingObserver)this); 

        // open connection to server, must run on the same machine
        ccm.open("127.0.0.1"); 

        // send first event to the server.
        ccm.sendDataEvent(new DataEvent(new Integer(1), "Ping"));
    }

    // starts up the client
    public static void main(String[] args) {
        TestClient tc = new TestClient(); 
    }

    // receives all data events
    public void receiveEvent(DataEvent de) { 
        // received data from the server
        System.out.println("Server says " + de.getData().toString()); 

        // send another ping
        ccm.sendDataEvent(new DataEvent(new Integer(1), "Ping"));
    }

    // receives all communication events
    public void receiveEvent(CommunicationEvent ce) {
        switch (ce.getEventId()) {
            case (CommunicationEvent.CONNECTION_ESTABLISHED):
                System.out.println("connection opened!");
                break;
            case (CommunicationEvent.CONNECTION_BROKEN):
                System.out.println("connection broken");
                break;
            case (CommunicationEvent.CONNECTION_CLOSED):
                System.out.println("connection closed");
                break; 
            case (CommunicationEvent.CONNECTION_START_LISTENING):
                System.out.println("start listening");
                break; 
            case (CommunicationEvent.CONNECTION_STOP_LISTENING):
                System.out.println("stop listening");
                break; 
        }
    }
}

The implemented protocol in this case is very simple: The client sends a ping message to the server, the server prints the message and returns a pong message. Every time the client receives a messages, it sends a ping to the server. Therefore this ends in an endless communication, with the client sending pings and the server answering with pongs.


Further Examples

Further examples using the CSCC can be found in the source code packages {@link ch.unizh.ifi.comm.test}, {@link ch.unizh.ifi.comm.examples.chatsystem} and {@link ch.unizh.ifi.comm.examples.simplesystem}.


Source Codes

After getting the source codes, the source codes can be compiled by running ant in the root of the project directory. Use the following ant targets:

Ant Target Description
clean  Cleans the build directories
clean_compile  Cleans the build directory and compiles the source code
compile  Compiles the source code
cvs-update Updates project (src dir)
doall  Does all the tasks
javadoc Generates the API documentation
javadoc-private Generates the API documentation with containing also the description of private elements
prepare  Prepares the execution structures
release  Builds a .jar file which is ready to use as a library in the class path.
run-client  Runs the example chat program (client)
run-server Runs the example chat program (server)


 


Glossary of the C/SCC:

Client The cllient is a consumer of a service which is provided by the server. The client has to connect over a remote connection to the server for getting the provided services.
Client Identification Object The identification object is used at the server side to retrieve realte a client to a specific data or communication event. For the clients, it's sometimes necessary  to know which client is the originator of a communication or data event. The client that caused a communication event is identified by calling the method {@link ch.unizh.ifi.comm.CommunicationEvent#getEventId()}.
Communication Event The event that occurs, when the state of the communication connection changes.
Data Event The event that occurs, every time data is received from one of the communication partner.
Process/Thread A process/thread is the execution of code. Several processes can run in parallel or concurrently.
Server The server provides remote services to one or more clients.


Project History

$Date: 2006/12/02 16:19:21 $
$Author: smeier $
$Revision: 1.1 $
Project History:
Date Version What
2003-05-16 0.99 Preview version for the tutors of the SOPRA 2003
2003-05-19 0.999 Beta version of the component for students (missing overview document)
2003-05-20 1.0 First official version (added some overview documentation)
2003-05-28 1.001 Some corrections in the examples, correction of some documentation documents.
2003-06-12 1.1 Corrections in the documentation.
Some improvements of the internal components. Now it is allowed to receive data events without synchronized statements.
Problems caused by serialization exceptions are shown by throwing an exception.
New class {@link ch.unizh.ifi.comm.CsccException} introduced.
{@link ch.unizh.ifi.comm.ServerConnectionManager#dispose()} cleans up all used resources after using the server component.
2003-06-15 1.2 Under some circumstances, version 1.1 (and earlier) of CSCC, a deadlock can be caused. This is problem is solved now.
CSCC is know optimized to transmit also bulk objects, the transmition is now much faster, than in the versions before.
2003-06-27 1.21 A bug in the {@link ch.unizh.ifi.comm.ServerConnectionManager#dispose()} method was removed which caused the system not to dispose the socket listener thread after calling this method. The exception handling is now much better and contains more meaningful exception descriptions.
2004-12-13 1.22 Some comments corrected, some obsolete code removed.
2006-10-23 1.23 Comments revised.