Updating a two-way java messenger chat to allow multiplie users

912 views Asked by At

I just finished an youtube series about creating an instant messenger chat, which works between the server and one client through sockets. However, I'd like to improve this chat so multiple clients can connect at the same time.

The server works fine, and so does the first client. On the first client window I open, I get the messages:

Attempting connection...
Connected to 127.0.0.1
Streams are now setup!
SERVER - You are now connected!

But on the second client window I open, the streams doesn't get setup, and I only get the following messages

Attempting connection...
Connected to 127.0.0.1

I've heard that I need to multithread to fix this problem, however I'm not quite sure how I'd do that.

These are my files:

ChatServerTest

import javax.swing.JFrame;

public class ChatServerTest {
    public static void main(String[] args) {
        ChatServer server =  new ChatServer();
        server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        server.startRunning();
    }
}

ChatServer

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ChatServer extends JFrame {

    private JTextField userText;
    private JTextArea chatWindow;
    private ObjectOutputStream output;
    private ObjectInputStream input;
    private ServerSocket server;
    private Socket connection;

    int port = 1234; // Test port

    //constructor (GUI)
    public ChatServer() {
        super("Instant Messenger (Server)");
        userText = new JTextField();
        userText.setEditable(false);
        userText.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent event){
                        sendMessage(event.getActionCommand());
                        userText.setText("");
                    }
                }
            );
        add(userText, BorderLayout.NORTH);
        chatWindow = new JTextArea();
        add(new JScrollPane(chatWindow));
        setSize(300,150);
        setVisible(true);
    }

    //set up and run the server
    public void startRunning(){
        try{
            server = new ServerSocket(port);
            while(true){
                try{
                    waitForConnection();
                    setupStreams();
                    whileChatting();
                }catch(EOFException eofException){
                    showMessage("\nServer ended the connection!");
                }finally{
                    closeCrap();
                }
            }
        }catch(IOException ioException){
            ioException.printStackTrace();
        }
    }

    //wait for connection, then display connection information
    private void waitForConnection() throws IOException{
        showMessage("Waiting for someone to connect... \n");
        connection = server.accept();
        showMessage("Now connected to " + connection.getInetAddress().getHostName());
    }

    //get stream to send and receive data
    private void setupStreams() throws IOException{
        output = new ObjectOutputStream(connection.getOutputStream());
        output.flush();
        input = new ObjectInputStream(connection.getInputStream());
        showMessage("\nStreams are now setup!\n");
    }

    //during the chat conversation
    private void whileChatting() throws IOException{
        String message = "You are now connected!";
        sendMessage(message);
        ableToType(true);
        do{
            try{
                message = (String) input.readObject();
                showMessage("\n" + message);
            }catch(ClassNotFoundException classNotFoundException){
                showMessage("\nERROR: Not a string");
            }
        }while(!message.equals("CLIENT - END"));
    }

    private void closeCrap(){
        showMessage("\nClosing connections...\n");
        ableToType(false);
        try{
            output.close();
            input.close();
            connection.close();
        }catch(IOException ioException){
            ioException.printStackTrace();
        }
    }

    //send a message to client
    private void sendMessage(String message) {
        try{
            output.writeObject("SERVER - " + message);
            output.flush();
            showMessage("\nSERVER - " + message);
        }catch(IOException ioExcenption){
            chatWindow.append("\nERROR: Can't send message");
        }
    }

    //updates chatWindow
    private void showMessage(final String text){
        SwingUtilities.invokeLater(
            new Runnable(){
                public void run(){
                    chatWindow.append(text);

                }
            }
        );
    }

    //let the user type stuff into their box
    private void ableToType(final boolean tof) {
        SwingUtilities.invokeLater(
            new Runnable(){
                public void run(){
                    userText.setEditable(tof);
                }
            }
        );
    }

}

ChatClientTest

import javax.swing.JFrame;

public class ChatClientTest {
    public static void main(String[] args) {
        String IP = "127.0.0.1";
        ChatClient client;
        client = new ChatClient(IP);
        client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        client.startRunning();
    }
}

ChatClient

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ChatClient extends JFrame {

    private JTextField userText;
    private JTextArea chatWindow;
    private ObjectOutputStream output;
    private ObjectInputStream input;
    private String message = "";
    private String serverIP;
    private Socket connection;

    int port = 1234; // Test port

    //constructor
    public ChatClient(String host){
        super("Instant Messenger (Client)");
        serverIP = host;
        userText = new JTextField();
        userText.setEditable(false);
        userText.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    sendMessage(event.getActionCommand());
                    userText.setText("");
                }
            }
        );
        add(userText, BorderLayout.NORTH);
        chatWindow = new JTextArea();
        add(new JScrollPane(chatWindow), BorderLayout.CENTER);
        setSize(300, 150);
        setVisible(true);

    }

    //connect to server
    public void startRunning(){
        try{
            connectToServer();
            setupStreams();
            whileChatting();
        }catch(EOFException eofException){
            showMessage("\nClient terminated connection.");
        }catch(IOException ioException){
            ioException.printStackTrace();
        }finally{
            closeCrap();
        }
    }

    //connect to server
    private void connectToServer() throws IOException{
        showMessage("Attempting connection...\n");
        connection = new Socket(InetAddress.getByName(serverIP), port);
        showMessage("Connected to " + connection.getInetAddress().getHostName());
    }

    //set up streams to send and receive messages
    private void setupStreams() throws IOException{
        output = new ObjectOutputStream(connection.getOutputStream());
        output.flush();
        input = new ObjectInputStream(connection.getInputStream());
        showMessage("\nStreams are now setup!");
    }

    //while chatting with server
    private void whileChatting() throws IOException{
        ableToType(true);
        do{
            try{
                message = (String) input.readObject();
                showMessage("\n" + message);
            }catch(ClassNotFoundException classNotFoundException){
                showMessage("\nUnknown object type");
            }
        }while(!message.equals("SERVER - END"));
    }

    //close the streams and sockets
    private void closeCrap(){
        showMessage("\nClosing down...");
        ableToType(false);
        try{
            output.close();
            input.close();
            connection.close();
        }catch(IOException ioException){
            ioException.printStackTrace();
        }
    }

    //send messages to server
    private void sendMessage(String message){
        try{
            output.writeObject("CLIENT - " + message);
            output.flush();
            showMessage("\nCLIENT - " + message);
        }catch(IOException ioException){
            chatWindow.append("\nMessage failed to send.");
        }
    }

    //change/update chatWindow
    private void showMessage(final String message){
        SwingUtilities.invokeLater(
            new Runnable(){
                public void run(){
                    chatWindow.append(message);

                }
            }
        );
    }

    //gives user permission to type text into the text box
    private void ableToType(final boolean tof){
        SwingUtilities.invokeLater(
            new Runnable(){
                public void run(){
                    userText.setEditable(tof);      
                }
            }
        );
    }

}

In advance I appreciate and thank any kind of response I can get :)

1

There are 1 answers

0
Seelenvirtuose On BEST ANSWER

Althoug a bit too broad for SO, I will give you a strategy for how to deal with this kind of server-client programs. For more information just hit your favorite search machine. There must be tons of articles.

The basic strategy of a server handling multiple client requests is this:

while (true) {
    accept a connection;
    create a thread to deal with the client;
    start that thread;
}

This way, the newly created (and started) thread will run concurrently. That lets the thread running this server code come back to the while (true) loop and stop at the "accept a connection" line, effectively waiting for the next client.

So, basically you should have something like

while (true) {
    connection = server.accept();
    Thread clientHandler = new Thread(new ClientHandler(connection));
    clientHandler.start();
}

That requires a class ClientHandler implementing Runnable which does all the client communication.

Note, that there are many parts of your program that should be refactored. Your class ChatServer is doing way too much work. It is even extending JFrame. My suggestion plays in the same league and is only a beginning path. Usually, you should not create a new thread for each incoming client request, but use a limited thread pool.