Aniseedwolf
Aniseedwolf

Reputation: 1

How do I call a method in a specific socket thread using "implements Runnable"?

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:

https://github.com/ico77/chat-server-client/blob/master/src/main/java/hr/ivica/chat/server/ChatServer.java

which can take advantage as running the server as a threaded pool server.

However the sendToAll function in this case writes directly to the PrintWriters 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?

Upvotes: 0

Views: 2389

Answers (2)

Krle
Krle

Reputation: 70

I am not sure if I understood your question. The short answer: if you want to make clientThread a Runnable, just do it and then change the line

  (threads[i] = new clientThread(clientSocket, threads)).start();

into

  (threads[i] = new Thread(new clientThread(clientSocket, threads))).start();

If you look at the documentation: http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#Thread(java.lang.Runnable)

Threads accept objects with Runnable supertype.

The long answer: you should not store threads directly but make an abstraction that represents a Client on the server side. This abstraction should encapsulate functionality for communication. That way, if you want to implement a different communication library you can easily subclass it and avoid breaking the open-close principle.

https://en.wikipedia.org/wiki/Open/closed_principle

Good luck.

Upvotes: 0

durron597
durron597

Reputation: 32343

Your big problem is that you are using the Threads themselves directly as the communication layer between the Server and the Client threads, something you should not do.

Instead, create your own interface Message objects that communicate the different information between the threads, and use LinkedBlockingQueue to process them.

You should probably have:

  • One queue for the server to receive messages
  • Depending on your implementation, one queue for each of the client threads to receive messages from the server, or one queue that's shared (if it's designed so that any thread can handle any message).

So you might do something like:

Message:

public interface Message {
  accept(Server server);
}

Disconnection Message (I'm just going to do one):

public class DisconnectionMessage implements Message {
  String username;

  public void accept(Server server) {
    server.handleMessage(this);
  }
}

Server Runnable:

public void run() {
  while(isServerOnline()) {
    Message clientMessage = queue.poll();
    clientMessage.accept(this);
  }
}

public void handleMessage(DisconnectionMessage msg) {
  // code
}

public void handleMessage(ConnectionMessage msg) {
  // code
}

etc.

Client Runnable:

private final Socket socket;
private final BlockingQueue<Message> queue;

public Client(BlockingQueue<Message> queue, Socket socket) {
  this.queue = queue;
  this.socket = socket;
}

public void run() {
  while(true) {
    Message msg = receiveMessage();
    queue.offer(msg);
  }
}

Upvotes: 1

Related Questions