I have a working Java Server (Although a tad rough around the edges) which contains 3 main classes.
The first class runs the server and gets the socket to listen on a port and passes new connections to a client handler.
The second class is a threaded client handler
The third is a protocol class which is called from the client handler and processes information. Once the information is processed, the protocol class returns a processed or formatted response back to the client handler to pass to the client.
The advantage is that the second class just needs to be loaded with what is acceptable data to accept from the socket. The data can be passed to the protocol handler, and the protocol handler can be loaded with whatever protocol you want the server to use to talk to the client.
In this instance I have loaded in a telnet-based chat class.
If, for example, someone leave the chat the client handler class may execute code such as:
for (i = 0; i < currentClientsConnected; i++) {
if(threads[i] != null && threads[i] != this) {
outputLine = threads[i].serverprotocol.processInput("** " + username + " has left the room **");
threads[i].out.printf(outputLine);
}
}
This passes "** [username] has left the room **"
to the serverprotocol
class, which then returns the data in the best best way to transmit the message to the clients. In this case the serverprotocol
class formats the message with telnet control code which tells the client to re-draw the screen, add the new message and scroll up the existing current messages in the buffer.
I may also only want the client handler class to send message to sockets where the users are in certain chat rooms for example, so I will not want to always send to all the sockets.
In my code, this is Class 1 - the server class which accepts sockets with:
while (true) {
int i;
// Try and accept the connection
try {
clientSocket = serverSocket.accept();
// System.out.printf("Remote IP:");
// System.out.printf(clientSocket.getRemoteSocketAddress().toString());
// Find an unused socket if one is available
for (i = 0; i < maxClientsAllowed; i++) {
// If found create thread
if (threads[i] == null) {
(threads[i] = new clientThread(clientSocket, threads)).start();
break;
}
}
// If all sockets are taken
if (i == maxClientsAllowed) {
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.printf("Server too busy. Try later.\n");
out.close();
clientSocket.close();
}
} catch(IOException e) {
System.out.println(e);}
}
and Class 2 is a class which extends thread:
class clientThread extends Thread {
private String clientName = null;
private DataInputStream in;
private PrintWriter out;
private Socket clientSocket = null;
private final clientThread[] threads;
private int currentClientsConnected;
private serverprotocol serverprotocol;
public clientThread(Socket clientSocket, clientThread[] threads) {
this.clientSocket = clientSocket;
this.threads = threads;
currentClientsConnected = threads.length;
}
public void run() {
//stuff
}
}
I have been trying desperately to see if I can get this working using implements Runnable
instead, but I have had no luck in calling a thread's processInput
(or maybe that should read dataToBeProcessed
) method based on the instance number of the thread (Simply called i
in the code here).
The closest I have seen in:
which can take advantage as running the server as a threaded pool server.
However the sendToAll
function in this case writes directly to the PrintWriter
s associated to the socket via a HashMap
. The server does not let you send to individual protocol handler classes, or even to the individual ChatServerWorker
class instances. This means I can't, for example, send a message to socket 1 and 3 only and then a separate message to socket 2.
I can't find a single example online where an instance of a socket handler can be called without using extends Thread
.
Specifically,I want to keep the ability to use lines like:
threads[i].out.printf(outputLine);
or
if(threads[i].[class].[var] == 'something') {
// stuff
}
Where an integer can be used to reference the thread instance, or any class vars or methods used by that thread.
Am I missing something?