Table of Contents
How to Initialize a Server?
In order to initialize the server, you need to first implement your Server Protocol object, or use our default TIProtocol. Then you need to pass the Server Protocol object into the ClientHandler constructor and add responding/nonresponding job listeners to ClientHandler. After start() is called on ClientHandler, you successfully started the server.
int finalPort = 9999; // server listens or port 9999
String[] allowedCiphers = {"TLS_RSA_WITH_AES_128_CBC_SHA"}; // create ciphers
String keyStorePassword = "password"; // create keyStore password
String keyStoreFilePath = ".//res//keystore"; // create keyStore file path
TIProtocol tiProtocol = new TIProtocol(allowedCiphers,
finalPort, keyStorePassword, keyStoreFilePath); // create protocol
ClientHandler ch = new ClientHandler(tiProtocol); // create client handler
AbstractRespondingListener listener = new YourRespondingListener(); // create listener
ch.addRespondingListener(listener); // add listener to client handler
ch.start(); // start server
What is a Server Protocol?
Server Protocol object defines the workflow of the server and abstracts away the server-client connection. The Server Protocol object needs to implement a ServerProtocolInterface, which supports startServer, stopServer, retrieveJob, and sendReply.
What is a ClientHandler?
ClientHandler deals with jobs extracted from protocol object, handles jobs using specific job handlers, and sends serverReply back to the client.
What is a TIProtocol and What are Implemented there?
TIProtocol is the default protocol object our team provides for you to easily deploy your own project. It implements ServerProtocolInterface and Runnable so that when we call listen() on ClientHandler side, the protocol object can start the server on one thread and constantly handles jobs on another.
- TIProtocol initialization:
- selectedCiphers: an array of strings defining the selected ciphers that are going to use inside protocol
- finalPort: the port that SSL ServerSocket is going to be bound to
- keyStorePassword, keyStoreFilePath: arguments needed to create a KeyStore object using PKCS12 format
- TIProtocol startServer: accept the clientSocket and encrypt it; start a new thread and pass the clientSocket to TIProtocolFSM to deal with actual control flow.
- TIProtocol retrieveJob: pop the front element off _jobBuffer
- TIProtocol sendReply: resumes the waiting client connection and sends serverReply back to the client
- TIProtocol stopServer: currently forcefully shutdown the server
Running Example
In our running example, we create multiple threads to handle multiple connections. Each connection will be bound to port 9999. We set up the ciphers, keystore password, and keystore file path for each connection and start it.
Keystore File Path
To create the keystore file path, we add a keystore file under ./sandbox/res. Before running the example, make sure you configure the working directory to ./sandbox.
Client Version and Client Version Checker
To use our client version and client version checker, we first create a new client checker. We will be using the newly defined checker in Verifying Client Version. After adding this new class in Android Studio, we pass the checker as an argument when instantiating the tiProtocol.
*In our running example, our client version authenticator accepts all client versions out of simplicity. In the case where we want our version authenticator to be more particular, we would create valid client versions and add them to the client version authenticator before passing the authenticator into the tiProtocol instantiation.
User Identifier Authenticator
We instantiate a user identifier authenticator, newly defined in Authenticating Client ID. After, we pass it into the tiProtocol instantiation.
*In this example, our identifier authenticator accepts all users. In the case where we want out authenticator to be more particular, we would have to create valid user IDs and add them into the user authenticator.
Code
The complete code of the running example’s server is shown below.
package com.example;
import java.util.logging.Filter;
import java.util.logging.LogRecord;
import edu.berkeley.telemonitoring.server.clientservices.ClientHandler;
import edu.berkeley.telemonitoring.server.clientservices.tiprotocol.TIProtocol;
public class RunningExampleServer implements Filter {
public static void main(String[] args) throws Exception {
int port = 9999;
if (args.length > 0 ) {
port = Integer.parseInt(args[0]);
}
final int finalPort = port;
new Thread(new Runnable() {
public void run() {
while (true) {
try {
String[] allowedCiphers = {"TLS_RSA_WITH_AES_128_CBC_SHA"};
String keyStorePassword = "password";
String keyStoreFilePath = ".//res//keystore";
final AcceptAllClientVersions clientVersionChecker = new AcceptAllClientVersions();
final AcceptAllUsers idAuthenticator = new AcceptAllUsers();
TIProtocol tiProtocol = new TIProtocol(allowedCiphers, finalPort,
keyStorePassword, keyStoreFilePath, clientVersionChecker, idAuthenticator);
final ClientHandler ch = new ClientHandler(tiProtocol);
// Battery Responsing listener
BatteryListener batteryL = new BatteryListener();
ch.addRespondingJobListener(batteryL);
ch.start();
} catch (Exception e) {
DataWriter.writeToErrorLog(e); // Log it, but try to recover
}
}
}
}).start();
}
public ExampleServer() {
super();
}
public boolean isLoggable(LogRecord record) {
return true;
}
}
Create a file called myServer.java with the code up above and add it into your working project in Android Studio. Now you have a fully functioning server!
You can move onto the client side of the running example here.