Introduction CSCC

Introduction CSCC is a small and lightweight Java component which is a simple substitute for RMI, as it allows to transmit any serializable carrier data.

See
          Description

Packages
net.sf.cscc The package net.sf.cscc contains the classes and interfaces for the Client/Server Communication Component (CSCC).
net.sf.cscc.examples.chatsystem The package net.sf.cscc.examples.chatsystem contains a Server and a Client of a chatsystem demonstrating the usage of Client/Server Communication Component (CSCC).
net.sf.cscc.examples.simplesystem The package net.sf.cscc.examples.simplesystem contains a small application demonstrating how to send data from multiple clients to a server using the Client/Server Communication Component (CSCC).
net.sf.cscc.test  

 

Introduction

CSCC is a small and lightweight Java component which is a simple substitute for RMI, as it allows to transmit any serializable carrier data. It allows an asynchronous data communication between one server and one or more clients. The communication is coordinated by a server-side queuing mechanism and is based on simple TCP-Sockets. In contrast to RMI, the component is simple to use as only a small interface for receiving data/communication events has to be implemented. Though, it is ideal for rapidely engineered/prototyped and small communication projects where RMI would be overkill. However, also the development of more sophisticated communication protocols is possible with this component. This component was successfully used in software engineering courses at the University of Zurich.

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 using a queuing of messages. The mechanism is transparent for the user of the CSCC and therefore the component is easy to use.

There are two different types of communication events that can occur at the client- or the server-side. 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 represented by an object of the class DataEvent. The second event type occurs if one or more nodes in the communication network are informed about the state of the coommunication, e.g. if a client loses the connection or a new client joins to the server. 

The server has to implement a logic for interpreting the events. Events might be processed by the server or they might be forwarded to one or more clients. Thus, the CSCC provides the communication between one server and one to several clients. However, high-level protocol has to be implemented by the component user.


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 net.sf.cscc:

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


Receiving Data

In General

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

The two modes use different interfaces. The interface DataEventObserver is used by the pull mode and just receives the event, but does not deliver the DataEvent object directly. When implementing the DataEventObserver interface, a particular class has to fetch the event manually out of the event queue. Therefore the implementing class is responsible, that the event is removed from the queue, after all observers received the event. Furthermore, additional threads have to be introduced for checking the queue after a certain time interval, therefore the usage of the CSCC in the pull mode is discouraged. Moreover, the pull mode is not very convenient and efficient and it may cause a synchronization problem. 

Hence, the use of CSCC in push mode should be preferred. The push mode needs the implementation of the interface DataEventReceivingObserver. This interface delivers the data event with the notification of the data event occurs. The implementation of the DataEventReceivingObserver interface by all participants of the communication ensures that all observers consume the data events immediately after their occurence and therefore, the data event is automatically removed from the data event queue. Thus in the following, only the use of the push mode of the CSCC is discussed.


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 Integer object containing a 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 one participating client to another is easy. After instantiating a ClientConnectionManager object at the client's side and opening a connection to a server (by using the method ClientConnectionManager.open(String name)), the data can be sent to the target by using the method ClientConnectionManager.sendDataEvent(DataEvent de). The given DataEvent contains the data which is asynchronously transmitted to the target.

Server Side Sending

There are two different methods to send DataEvent objects to the clients. The first method is used to broadcast an event object to all connected clients. This is done by using the ServerConnectionManager.sendBroadCastEvent(DataEvent de) method. The method ServerConnectionManager.sendDataEvent(DataEvent de, Object clientId) sends the message to client. The parameter 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 represented by the CSCC class CommunicationEvent. Five different types of events can occur which are described by the following constants:

The event type can be queried by calling the method CommunicationEvent.getEventId(). For receiving communication events, an object of the type CommunicationEventObserver has to be added to the communication observer list. This is done by calling the method ServerConnectionManager.addObserver(net.sf.cscc.CommunicationEventObserver o) for registering the server-side communication observer and by calling the method ClientConnectionManager.addObserver(net.sf.cscc.CommunicationEventObserver o) for registering the client-side communication observer respectively.

Under some circumstances, it is necessary to identify the client which caused the communication event. Therefore the method CommunicationEvent.getData() returns an identifier object. This object is used to identify the client uniquely. In this implementation this object is a Integer object containing a unique integer number denoting the client. The events occuring contain client ids are: CommunicationEvent.CONNECTION_ESTABLISHED, CommunicationEvent.CONNECTION_BROKEN and  CONNECTION_CLOSED.


Code Example

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

Server Side

For creating a server, follow these steps:

  1. Instantiate a new ServerConnectionManager object. An object of this class handles the communication of a server:

ServerConnectionManager scm = new ServerConnectionManager(2510);

This statement instantiates a new ServerConnectionManagerobject which uses the TCP/IP port 2510 listening for client connections.

  1. The listening for new client connections is started by calling

    scm.beginListening();  
  1. The server begins now to listen for new client network connections.

  2. For receive data events from the clients, a class has to implement the interface DataEventReceivingObserver:

    public class MyServerDataEventReceiver implements DataEventReceivingObserver {
        ...

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

  3. The observer has to be registered at the server connection manager with:

    scm.addObserver(new MyServerDataEventReceiver());


  4. To receive communication events (i.e. notifications about changes in the state of the communication channel of two participating nodes), the interface CommunicationEventObserver has to be implemented:

    public class MyServerCommunicationEventReceiver implements CommunicationEventObserver {
        ...

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

  5. The observer has to be registered at the server connection manager with:

    scm.addObserver(new MyServerDataEventReceiver());

Client

For creating a client, these steps have to be followed:

  1. A new ClientConnectionManager object has to be instantiated. An object of this class handles the communication between a client and a server:

ClientConnectionManager ccm = new ClientConnectionManager(2510);

This statement instantiates a new ClientConnectionManagerobject which uses the TCP/IP port 2510 for connections to the server.

  1. A new connection to the server is opened by:

    ccm.open("127.0.0.1");

  2. 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 specified by an IP address or DNS name.
     
  3. To receive data events from the server, a class has to implement the interface DataEventReceivingObserver:

    public class MyClientDataEventReceiver implements DataEventReceivingObserver {
        ...

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

  4. The observer has to be registered at the server connection manager with:

    scm.addObserver(new MyClientDataEventReceiver());


  5. To receive communication events (changes of the communication state), the interface CommunicationEventObserver has to be implemented:

    public class MyClientCommunicationEventReceiver implements CommunicationEventObserver {
        ...

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

  6. The observer has to be registered at the server connection manager with:

    scm.addObserver(new MyClientCommunicationEventReceiver());

 

Full Example

A full example of a client/server application looks for instance like the following:

/* Server */
import net.sf.cscc.ServerConnectionManager;
import net.sf.cscc.DataEventReceivingObserver;
import net.sf.cscc.DataEvent;
import net.sf.cscc.CommunicationEventObserver;
import net.sf.cscc.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 net.sf.cscc.ClientConnectionManager;
import net.sf.cscc.DataEvent;
import net.sf.cscc.DataEventReceivingObserver;
import net.sf.cscc.CommunicationEvent;
import net.sf.cscc.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 protocol implemented in this example is very simple: The client sends a ping message to the server, the server prints the message and returns a pong message to the client. Every time the client receives a messages, it sends a ping to the server. Therefore client and the server sends endlessly pings and pongs respectively.


Further Examples

Further examples using the CSCC can be found in the source code packages net.sf.cscc.test, net.sf.cscc.examples.chatsystem and net.sf.cscc.examples.simplesystem.


Source Codes

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

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 CSCC:

Client The client is a consumer of a service which is provided by the server. The client has to connect over a connection to the server for getting the provided services.
Client Identification Object The identification object is used at the server side to retrieve the client from which a specific data or communication event originates. The client id can of a communication event can be retrieved by calling the method CommunicationEvent.getEventId().
Communication Event A communication event occurs when the state of the communication connection changes.
Data Event A data event occurs every time data is received from one of the communication partners.
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: 2007/07/02 21:26:16 $
$Author: reode_orm $
$Revision: 1.2 $
Project History:
Date Version What
2003-05-16 0.99 Preview version.
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 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.
The new class CsccException was introduced. The method
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 can cause a deadlock. This problem is solved now.
CSCC is know optimized to transmit also bulk objects, the transmission is now much faster for bulk object.
2003-06-27 1.21 A bug in the 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.
2007-07-01 1.24 Adaptions for the publication on sourceforge.net.
2007-07-02
1.25
Some comments corrected.




Copyright © 2003, Silvio Meier and Tobias Reinhard. All Rights Reserved.