Menu

Server Manual

Janet Hunt

Server Service

Strategy

The Server class provides the facility to receive and answer connection attempts to a system coming in from the Internet. The server owns a socket which only deals with the reception of new connections; this is called the "server-socket". Upon incoming requests it creates new connections, which have their own sockets, and renders them to the application for communication. Thus a multitude of connections to different remote clients can originate from a single server instance. Even for a single peer-to-peer communication setup you have to use the Server class to receive a call from the partner. In that generic strategy only two things have to be established: a definition of the server's Internet service address and a method to fetch new connections from the server. In short, the Server class functions as a Connection broker and does not communicate itself. In contrast, all data communication with the clients takes place on the connections rendered by the server.

Preparations
A Server instance can be created either bound to a local port and Internet address or unbound. However, a Server must be bound before it can be started and become operational.


// creates an unbound server instance
Server server = new Server();

// creates a server bound to port 42900 on the wildcard Internet address
Server server = new Server(42900);

Server server = new Server();
server.bind(42900);

// creates a server bound to a random free port address on the
// wildcard Internet address
Server server = new Server();
server.bind(0);

// creates a server bound to an Internet socket address
SocketAddress address = new InetSocketAddress("localhost", 42900);
Server server = new Server(address);

Server server = new Server();
server.bind(address);

// add a listener to server events
ServerListener serverListener = new OurServerListener();
server.addListener(serverListener);

// start the server
server.start();

Services of the Server

The Server class provides autonomous event dispatching within a dedicated thread per instance. Events are distributed to listeners implementing the ServerListener interface.

Accepting Connections

The ServerListener interface defines some frugal information events for the application, such as when a connection is added to or removed from the server's registry of alive connections. Its most prominent feature, however, is the signalling of new connections that can be taken away from the interface for further application use (via connectionAvailable(ServerConnection)). ServerConnection is more or less a tagging class for Connection to make it discriminable from Client. It has an additional function start(), that is to start activity of the connection. This is useful, first to allow some space to set up individual parameters for the connection before it starts execution, second to also allow for rejecting a connection, instead of starting it, by the reject() method. Connections that are signalled by the Server class are layer-verified, that means they have identified as originating from a matching JennyNet software package.

A typical code for dealing with incoming connections then would look like this:


  server.addListener( new DefaultServerListener() {

     @Override
     public void connectionAvailable(ServerConnection connection) {
        // we think about whether we can handle this connection
        boolean isAcceptable = true;
        // then we accept or reject it
        try {
           if (isAcceptable) {
              // we can set some parameter, like e.g. the file-root-path
              connection.getParameters().setFileRootDir(rootDirectory);
              // and add our listener to connection events
              connection.addListener(ourConnectionListener);  
              connection.start();
           } else {
              connection.reject();
           }
        } catch (IOException e) {
           e.printStackTrace();
        }
     }
  });

However, there is a second way to getting to the incoming connections, and that is by the server's ServerConnection accept() method. This method works much like a server socket's accept method and returns a new connection when it has arrived and validated, while blocking the calling thread until such an event occurs. This Accept conception follows a different philosophy, compared to the above described event driven Listener method. While in "listening" network events determine time and frequency of application's reactions to incoming connections, in "accepting" them application retains full control of when it comes around to collect more connections. A server can run only in one of the two rendering methods.

A code snippet for dealing connections within a dedicated server thread and with the Accept method could look like this:


       try {
          // create the server and set ACCEPT method
          Server server = new Server(portNumber);
          server.setSignalMethod(SignalMethod.Accept);
          server.setAcceptQueueCapacity(150);
          server.start();

          // loop over reception queue of incoming connections (blocking)
          while (operating) {
             ServerConnection connnection = server.accept(0);
             connection.getParameters().setFileRootDir(rootDirectory);
             connnection.addListener(ourConnectionListener);

             // integrate connection into application
             ....

             connnection.start();
             connnection.sendPing();

             System.out.println("--- CONNECTION ACCEPTED from : " 
                    + connnection.getRemoteAddress() );
          }

       } catch (InterruptedException e) {
       } catch (IOException e) {
           e.printStackTrace();
           System.err.println("Could not listen on port " + portNumber);
           System.exit(-1);
       }

Keeping the Memory (Connection Register)

After starting a Connection rendered by a Server instance, they function logically independent of the server and from each other. For a server application it is not even required to keep references to open connections, and it could possibly just react to connection events reported at the ConnectionListener interface. Where a more active role is played by the server, it would suffice to keep the UUID identifiers of the connections involved. This is made possible as the Server class of JennyNet keeps a register of open connections, and the application can at any time retrieve connections, single by their UUID value, or collective as an array of registered connections. When a connection closes, no matter what the cause, it is automatically removed from the registry. To give the user control of this membership, connections may be removed from or added to this registry arbitrarily with methods removeConnection(Connection) and addConnection(Connection).


/** Send a priority text to given connection (UUID). */
boolean sendStringImmediate(UUID connection_UUID, String text) {
    Connection c = server.getConnection(connection_UUID);
    if (c != null) {
        c.sendObject(text, true);
        return true;
    } 
    return false;
}

Multiplex Anyone ..?

Taking advantage of its registry, the Server class offers methods to send objects or actions to the entire set of registered connections in a single call. The following send actions can be performed on the set of connections: PING, TEMPO, OBJECT, OBJECT-PRIORITY, FILE.


Related

Wiki: Client Manual
Wiki: User Manual