CODE

Server Main

public class ServerMain {
public static void main(String[] args) {
    //run locally using "telnet localhost [port]" in terminal or Powershell
    int port = 8818;
    Server server = new Server(port);
    //Run thread from Server object
    server.start();
    }

}

Server Worker

public class ServerWorker extends Thread{

private final Socket clientSocket;
private final Server server;
private String ulogin = null; //username for this serverworker
private OutputStream os;
public ServerWorker(Server server, Socket clientSocket){
    this.server = server;
    this.clientSocket = clientSocket;
}

@Override
public void run(){
    try {
        handleClientSocket();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public String getLogin(){
    return ulogin;
}

//method that works on thread
private void handleClientSocket() throws IOException, InterruptedException {
    //Bidirectional client server communication
    InputStream is = clientSocket.getInputStream();
    clientSocket.getOutputStream();
    this.os = clientSocket.getOutputStream();

    //BufferedReader to read line by line
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    String line;
    while ( (line = reader.readLine()) != null){
        //split line into array of tokens
        String[] tokens = line.split(" ");
        if (tokens != null && tokens.length > 0){
            String cmd = tokens[0]; //first word in line
            //RECOGNIZE COMMANDS
            //terminate connection on quit command
            if ("quit".equalsIgnoreCase(cmd) || "logoff".equals(cmd)){
                handleLogoff();
                break;
            } else if("login".equalsIgnoreCase(cmd)){
                handleLogin(os, tokens);
            } else if("msg".equalsIgnoreCase(cmd)){
                String[] tokensMsg = new String[tokens.length - 2];
                for (int i = 2; i < tokens.length; i++){
                    tokensMsg[i - 2] = tokens[i];
                }
                String toSend = String.join(" ",tokensMsg);
                handleMessage(tokens[1], toSend);
            } else if("join".equalsIgnoreCase(cmd)){
                handleJoin(tokens);
            } else if("leave".equalsIgnoreCase(cmd)){
                handleLeave(tokens);
            }
            else {
                String msg = "unknown command: " + cmd + "\n";
                os.write(msg.getBytes());
            }

            /* ECHO BOT
            String msg = "You typed: " + line + "\n";
            os.write(msg.getBytes());
            */
        }
    }

    //Output from server to client
    /*
    OutputStream os = clientSocket.getOutputStream();
    for(int i = 0; i<10; i++){
        os.write(("\nThe time now is " + new Date()).getBytes());
        Thread.sleep(1000);
    }
    */

    //close socket
    clientSocket.close();
}

private void handleLeave(String[] tokens) {
    if (tokens.length > 1){
        String topic = tokens[1];
        topicSet.remove(topic);
    }
}

public boolean isMemberOfTopic(String topic){
    return topicSet.contains(topic);
}

private void handleJoin(String[] tokens) {
    if (tokens.length > 1){
        String topic = tokens[1];
        topicSet.add(topic);
    }
}

//format: "msg" "login(user)" text...
//format: "msg" "#topic" body...
private void handleMessage(String sendTo, String text) throws IOException {
    //check if group/topic message
    boolean isTopic = sendTo.charAt(0) == '#';

    //Iterate through list of server workers to find worker login
    List