Search code examples
socketsjavafxhashmaphashtableserversocket

Send Message to All in Client Server Chat Application in JavaFX


I was able to send message to clients connected to server but when

  1. When Client Bunny sends message to Joel(receives message) and vice versa.
  2. When second time Bunny sends message to Joel (doesn't receive message).

How do I iterate hashtable loop again to send and receive messages.

Server Class

import java.io.*; //Input and output to read and write data.
import java.net.*; //serversocket and initaddress  
import java.util.Date; //to get date
import java.util.Enumeration; //enumeration
import java.util.Hashtable; // hashtable
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javafx.application.Application; //
import javafx.application.Platform; //platformrun later to append text
import javafx.event.EventHandler; // on click actions
import javafx.geometry.Insets; //insets
import javafx.geometry.Pos; //position
import javafx.scene.Scene; //scene
import javafx.scene.control.TextArea; //textarea
import javafx.scene.layout.StackPane; //stackpane
import javafx.stage.Stage; //stage
import javafx.stage.WindowEvent; //event close

public class Server extends Application { //class server

    static InetAddress inetAddress; //inetaddress
    private final Hashtable<Object, Object> outputStreams = new Hashtable(); //hashtable
    TextArea textarea = new TextArea(); //textarea
    static DataInputStream inputFromClient = null; //datainputstream
    static DataOutputStream outputToClient = null; //dataoutputstream
    static int clientNo = 0; //client connected
    static ServerSocket serverSocket; //server socket
    Hashtable<Object, Object> hmap = new Hashtable<>();

    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) { //Stage to show everything

        StackPane root = new StackPane();        //root add all the elements
        root.setPadding(new Insets(10, 10, 10, 10)); //padding
        root.setAlignment(textarea, Pos.CENTER); //positioning textarea to center
        textarea.setMaxSize(400, 400); //maxsize of textarea
        textarea.setWrapText(true); //wraptext for the textarea

        new Thread(() -> { //thread to hold the server for the connections
            try {
                // Create a server socket
                ServerSocket serverSocket = new ServerSocket(8001); //The server creates a server socket and, once a connection to a client is established, connects to the client with a client socket.
                textarea.appendText("MultiThreadServer started at " + new Date() + '\n'); //server started with date.

                while (true) { //accept multiple connections

                    // Listen for a new connection request
                    Socket socket = serverSocket.accept(); //listening for new connection from server port.
//                    inputFromClient = new DataInputStream(socket.getInputStream());
                    outputToClient = new DataOutputStream(socket.getOutputStream()); //outputstream to output to client
                    // Increment clientNo
                    clientNo++;

                    Platform.runLater(() -> {
                        // Display the client number
                        textarea.appendText("Starting thread for client " + clientNo + " at " + new Date() + '\n');

                        // Find the client's host name, and IP address
                        InetAddress inetAddress = socket.getInetAddress();
                        textarea.appendText("Client " + clientNo + "'s host name is " + inetAddress.getHostName() + "\n");
                        textarea.appendText("Client " + clientNo + "'s IP Address is " + inetAddress.getHostAddress() + "\n");
                    });

                    HandleAClient hd = new HandleAClient(socket);
                    hmap.put(socket, outputToClient);
                    hd.start();
//                    outputStreams.put(socket, outputToClient); //using hashtable to store client socket and outputstream

                    // Create and start a new thread for the connection
//                    new HandleAClient(this, socket);
                }
            } catch (IOException ex) {
                textarea.appendText(ex + " \n");
            }
        }).start();

        root.getChildren().addAll(textarea); // add UI elements to the root
        Scene scene = new Scene(root, 450, 400); // creating scene of size 450 width and height 500

        primaryStage.setTitle("Server"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage

        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { //close scene
            @Override
            public void handle(WindowEvent t) {
                primaryStage.close();
                Platform.exit();
                System.exit(0);
            }
        });
    }

    // Used to get the output streams
    Enumeration getOutputStreams() {
        return outputStreams.elements();
    }

    // Used to send message to all clients
    void sendToAll(String message) {
        // Go through hashtable and send message to each output stream
        for (Enumeration e = getOutputStreams(); e.hasMoreElements();) {
            DataOutputStream dout = (DataOutputStream) e.nextElement();
            try {
                // Write message
                dout.writeUTF(message);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    class HandleAClient extends Thread {

        private Socket socket; // A connected socket
        private String text = "";

        /**
         * Construct a thread
         */
        public HandleAClient(Socket socket) throws IOException {
            this.socket = socket;
        }

        /**
         * Run a thread
         */
        public void run() {
            try {
                // Create data input stream
                inputFromClient = new DataInputStream(socket.getInputStream()); //receive input from client

                textarea.appendText(new Date() + " Connection from  " + socket + "\n");//showing connection from client

                text = inputFromClient.readUTF(); // read string or message from client

//                serversocket.sendToAll(text); 
                Set set = hmap.entrySet();
                Iterator it = set.iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next();
                    System.out.println("key " + entry.getKey() + " : " + entry.getValue());
                    DataOutputStream dout = (DataOutputStream) entry.getValue();
                    dout.writeUTF(text);
                    dout.flush();
                }

                Platform.runLater(() -> {
                    textarea.appendText(new Date() + "         " + text + "\n");//append text
                });

            } catch (IOException e) {
                textarea.appendText("Error " + e + " \n");
                try {
                    this.socket.close(); //socket close
                } catch (IOException ex) {
                    textarea.appendText("Error " + e + "  \n");
                }
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) { //The keyword void simply tells the compiler that main( ) does not return a value.
        launch(args);
    }
}



Client Class

import java.io.*; //Input and output to read and write data.
import java.net.*; //serversocket and initaddress  
import javafx.application.Application; //
import javafx.application.Platform; //platformrun later to append text
import javafx.event.EventHandler; // on click actions
import javafx.geometry.Insets; //insets
import javafx.geometry.Pos; //position
import javafx.scene.Scene; //scene
import javafx.scene.control.TextArea; //textarea
import javafx.scene.layout.StackPane; //stackpane
import javafx.stage.Stage; //stage
import javafx.stage.WindowEvent; //event close

import javafx.scene.control.Button; // button
import javafx.scene.control.Label; //label
import javafx.scene.control.TextField; //textfield

public class Client extends Application {

    // IO streams
    static DataOutputStream toServer = null; //datainputstream
    static DataInputStream fromServer = null; //dataoutputstream
    Label usernamelabel = new Label("Set your name :"); //set name label
    Label textarealabel = new Label("Type your message in the textarea"); //text label
    Label textlabel = new Label("Enter Text :"); //enter text
    TextField textfield = new TextField(); //textfield
    TextField namefield = new TextField(); //name field
    Button send = new Button("Send"); //sendbutton
    TextArea messagefield = new TextArea(); //textarea
    static ServerSocket serverSocket; //serversocket

    // Override the start method in the Application class
    public void start(Stage primaryStage) throws IOException {

        StackPane root = new StackPane(); //stacpane
        root.setPadding(new Insets(10, 10, 10, 10));

        StackPane.setAlignment(usernamelabel, Pos.TOP_LEFT);
        namefield.setMaxWidth(200);
        StackPane.setAlignment(namefield, Pos.TOP_CENTER);

        StackPane.setMargin(textlabel, new Insets(30, 0, 0, 0));
        StackPane.setMargin(textfield, new Insets(30, 0, 0, 0));
        StackPane.setAlignment(textlabel, Pos.TOP_LEFT);
        textfield.setMaxWidth(200);
        StackPane.setAlignment(textfield, Pos.TOP_CENTER);

        StackPane.setAlignment(send, Pos.BOTTOM_CENTER);
        messagefield.setMaxSize(450, 250);
        StackPane.setAlignment(textarealabel, Pos.BOTTOM_CENTER);
        StackPane.setMargin(textarealabel, new Insets(0, 0, 35, 0));
        StackPane.setAlignment(messagefield, Pos.CENTER);

        root.getChildren().addAll(usernamelabel, namefield, send, messagefield, textarealabel, textlabel, textfield); //adding all the UI elements

        // Create a scene and place it in the stage
        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("Client"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage

        send.setOnAction(e -> { //send button action
            try {
                if (namefield.getText().trim().length() == 0) { //name field is empty
                    messagefield.appendText("Name field is empty. Please enter your name\n");
                } else if (textfield.getText().trim().length() == 0) { //message field is empty
                    messagefield.appendText("Message field is empty. Please enter your message to send\n");
                }
                // Send the radius to the server
                if (namefield.getText().trim().length() > 0 && textfield.getText().trim().length() > 0) {
                    toServer.writeUTF(namefield.getText().trim() + " : " + textfield.getText().trim());
                    toServer.flush();
                }

            } catch (IOException ex) {
                messagefield.appendText(ex+"   \n");
            }
        });

        try {
            // Create a socket to connect to the server
            Socket socket = new Socket("localhost", 8001); //socket with port number to connect server

            // Create an output stream to send data to the server
            toServer = new DataOutputStream(socket.getOutputStream());
            new ReceiveMessage(socket); // send socket receieve class
//            }
        } catch (Exception ex) {
            messagefield.setText(ex.toString() + '\n');
        }

        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { //scene close
            @Override
            public void handle(WindowEvent t) {
                Platform.exit();
                System.exit(0);
            }
        });
    }

    class ReceiveMessage implements Runnable { //class receive message

        private final Socket socket;//socket

        public ReceiveMessage(Socket socket) { // constructor
            this.socket = socket; //socket intializes
            Thread thread = new Thread(this);
            thread.setDaemon(true);
            thread.start();
        }

        public void run() {
            try {
                fromServer = new DataInputStream(socket.getInputStream()); //to read from server
                while (true) { // to continously receieve messages
                    // Get area from the server
                    String textmessage = fromServer.readUTF(); //read message from server
                    toServer.flush(); // flush
                    Platform.runLater(() -> {
                        messagefield.appendText(textmessage + " \n"); //append to textarea
                    });
                }
            } catch (IOException e) {
                messagefield.appendText("Error " + e);
            }
        }
    }

    public static void main(String[] args) { //The keyword void simply tells the compiler that main( ) does not return a value.
        launch(args);
    }
}

Server class executed and taking clients

Server Class

Client one connected

Running Client one

Client Two connected

Client two connected

Thank you.


Solution

  • The mistake I made in the code was after client sends message to server and sends message to all the clients connected to the server after that server connection is paused or halted I don't know why. I have used enumeration method to send messages to all the clients connected to the server. This is how I solved the issue. Hope my answer will help someone who is trying to implement client-server (multiple clients) chat application using thread.

    import java.io.*; 
    import java.net.*; 
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.Enumeration; 
    import java.util.Hashtable; 
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import javafx.application.Application;
    import javafx.application.Platform; 
    import javafx.event.EventHandler;
    import javafx.geometry.Insets; 
    import javafx.geometry.Pos; 
    import javafx.scene.Scene; 
    import javafx.scene.control.TextArea;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage; 
    import javafx.stage.WindowEvent; 
    
    public class Server extends Application { 
    
        static InetAddress inetAddress; 
        private final Hashtable<Object, Object> outputStreams = new Hashtable(); 
        TextArea textarea = new TextArea(); 
        static int clientNo = 0; 
        static ServerSocket serverSocket; 
        Hashtable<Object, Object> hmap = new Hashtable<>();
        ArrayList<Object> clients = new ArrayList<Object>();
    
    
        public void start(Stage primaryStage) {
    
            StackPane root = new StackPane(); 
            root.setPadding(new Insets(10, 10, 10, 10)); 
            root.setAlignment(textarea, Pos.CENTER);
            textarea.setMaxSize(400, 400); 
            textarea.setWrapText(true); 
    
            root.getChildren().addAll(textarea); 
            Scene scene = new Scene(root, 450, 400); 
    
            primaryStage.setTitle("Server"); 
            primaryStage.setScene(scene);
            primaryStage.show(); // Display the stage
    
            primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { 
                @Override
                public void handle(WindowEvent t) {
                    primaryStage.close();
                    Platform.exit();
                    System.exit(0);
                }
            });
    
            new Thread(() -> {
                listen();
            }).start();
        }
    
        private void listen() {
    
            try {
                // Create a server socket
                serverSocket = new ServerSocket(8001); 
                textarea.appendText("MultiThreadServer started at " + new Date() + '\n'); 
    
                while (true) { 
    
                    Socket socket = serverSocket.accept(); 
          inputFromClient = new DataInputStream(socket.getInputStream());
                    DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream()); 
                    clientNo++;
    
                    Platform.runLater(() -> {
    
                        textarea.appendText("Starting thread for client " + clientNo + " at " + new Date() + '\n');
    
    
                        InetAddress inetAddress = socket.getInetAddress();
                        textarea.appendText("Client " + clientNo + "'s host name is " + inetAddress.getHostName() + "\n");
                        textarea.appendText("Client " + clientNo + "'s IP Address is " + inetAddress.getHostAddress() + "\n");
                    });
    
                    hmap.put(socket, outputToClient);
                    new HandleAClient(socket);
    
                }
            } catch (IOException ex) {
                textarea.appendText(ex + " \n");
            }
    
        }
    
    
        Enumeration getOutputStreams() {
            return hmap.elements();
        }
    
    
        void sendToAll(String message) {
    
            for (Enumeration e = getOutputStreams(); e.hasMoreElements();) {
                DataOutputStream dout = (DataOutputStream) e.nextElement();
                try {
    
                    dout.writeUTF(message);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    
        void All(String text) throws IOException {
            Set set = hmap.entrySet();
            Iterator it = set.iterator();
            System.out.println("Text " + text);
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                System.out.println("key " + entry.getKey() + " : " + entry.getValue());
                DataOutputStream dout = (DataOutputStream) entry.getValue();
                dout.writeUTF(text);
                dout.flush();
            }
        }
    
        class HandleAClient extends Thread {
    
            private final Socket socket; // A connected socket
            private String text = "";
    
            /**
             * Construct a thread
             */
            public HandleAClient(Socket socket) throws IOException {
                this.socket = socket;
                start();
            }
    
            /**
             * Run a thread
             */
            public void run() {
                try {
    
                    DataInputStream inputFromClient = new DataInputStream(socket.getInputStream()); //receive input from client
                    while (true) {
    
                        textarea.appendText(new Date() + " Connection from  " + socket + "\n");
    
                        text = inputFromClient.readUTF(); 
    
                 serversocket.sendToAll(text); 
                        All(text);
    
                        Platform.runLater(() -> {
                            textarea.appendText(new Date() + "         " + text + "\n");
                        });
                    }
    
                } catch (IOException e) {
                    textarea.appendText("Error " + e + " \n");
                    try {
                        this.socket.close();
                    } catch (IOException ex) {
                        textarea.appendText("Error " + e + "  \n");
                    }
                    e.printStackTrace();
                }
            }
    
        }
    
        public static void main(String[] args) { //The keyword void simply tells the compiler that main( ) does not return a value.
            launch(args);
        }
    }