Friday, September 19, 2014

Java/JavaFX example: 1 server vs 30 clients

This video test how one Java/JavaFX server connected with 30 clients, all one on the same PC (running Linux) for testing.


reference:
Java/JavaFX Server link to Android Client
Java/JavaFX Client link to Android Server

Both server and client modified to suit for testing:

JavaFX_Server.java
package javafx_server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class JavaFX_Server extends Application {

    TextField textTitle;
    Label labelSys, labelPort, labelIp;
    TextArea textAreaMsg;
    CheckBox optWelcome;

    ServerSocket serverSocket;

    @Override
    public void start(Stage primaryStage) {

        textTitle = new TextField();
        labelSys = new Label();
        labelPort = new Label();
        labelIp = new Label();
        textAreaMsg = new TextArea();
        
        //Auto scroll to bottom
        textAreaMsg.textProperty().addListener(new ChangeListener(){

            @Override
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
                textAreaMsg.setScrollTop(Double.MAX_VALUE); 
            }
        });
        
        optWelcome = new CheckBox("Send Welcome when connect");
        optWelcome.setSelected(true);

        labelSys.setText(
            System.getProperty("os.arch") + "/"
            + System.getProperty("os.name"));
        labelIp.setText(getIpAddress());

        VBox mainLayout = new VBox();
        mainLayout.setPadding(new Insets(5, 5, 5, 5));
        mainLayout.setSpacing(5);
        mainLayout.getChildren().addAll(textTitle,
            labelSys, labelPort, labelIp,
            optWelcome, textAreaMsg);

        StackPane root = new StackPane();
        root.getChildren().add(mainLayout);

        Scene scene = new Scene(root, 300, 400);

        primaryStage.setTitle("Android-er: JavaFX Server");
        primaryStage.setScene(scene);
        primaryStage.show();

        Thread socketServerThread = new Thread(new SocketServerThread());
        socketServerThread.setDaemon(true); //terminate the thread when program end
        socketServerThread.start();
    }

    public static void main(String[] args) {
        launch(args);
    }

    private class SocketServerThread extends Thread {

        static final int SocketServerPORT = 8080;
        int count = 0;

        @Override
        public void run() {
            try {
                Socket socket = null;
                
                serverSocket = new ServerSocket(SocketServerPORT);
                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {
                        labelPort.setText("I'm waiting here: "
                            + serverSocket.getLocalPort());
                    }
                });

                while (true) {
                    socket = serverSocket.accept();
                    count++;
                    
                    //Start another thread 
                    //to prevent blocked by empty dataInputStream
                    Thread acceptedThread = new Thread(
                        new ServerSocketAcceptedThread(socket, count));
                    acceptedThread.setDaemon(true); //terminate the thread when program end
                    acceptedThread.start();

                }
            } catch (IOException ex) {
                Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        }

    }

    private class ServerSocketAcceptedThread extends Thread {

        Socket socket = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        int count;

        ServerSocketAcceptedThread(Socket s, int c) {
            socket = s;
            count = c;
        }

        @Override
        public void run() {
            try {
                dataInputStream = new DataInputStream(
                    socket.getInputStream());
                dataOutputStream = new DataOutputStream(
                    socket.getOutputStream());

                //If dataInputStream empty, 
                //this thread will be blocked by readUTF(),
                //but not the others
                String messageFromClient = dataInputStream.readUTF();

                String newMessage = "#" + count + " from " + socket.getInetAddress()
                    + ":" + socket.getPort() + "\n"
                    + "Msg from client: " + messageFromClient + "\n";
                
                Platform.runLater(new Runnable() {
                    
                    @Override
                    public void run() {
                        textAreaMsg.appendText(newMessage);
                    }
                });
                
                if (optWelcome.isSelected()) {
                    
                    String msgReply = count + ": " + textTitle.getText();
                    dataOutputStream.writeUTF(msgReply);
                }
                
            } catch (IOException ex) {
                Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                if (dataInputStream != null) {
                    try {
                        dataInputStream.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                if (dataOutputStream != null) {
                    try {
                        dataOutputStream.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }

    }

    private String getIpAddress() {
        String ip = "";
        try {
            Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
                .getNetworkInterfaces();
            while (enumNetworkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = enumNetworkInterfaces
                    .nextElement();
                Enumeration<InetAddress> enumInetAddress = networkInterface
                    .getInetAddresses();
                while (enumInetAddress.hasMoreElements()) {
                    InetAddress inetAddress = enumInetAddress.nextElement();

                    if (inetAddress.isSiteLocalAddress()) {
                        ip += "SiteLocalAddress: "
                            + inetAddress.getHostAddress() + "\n";
                    }
                }
            }
        } catch (SocketException ex) {
            Logger.getLogger(JavaFX_Server.class.getName()).log(Level.SEVERE, null, ex);
        } 

        return ip;
    }

}

Download.

JavaFX_Client.java
package javafx_client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class JavaFX_Client extends Application {

    Label labelSys;
    TextField welcomeMsg;
    Label labelAddress;
    TextField textAddress;
    Label labelPort;
    TextField textPort;
    Button buttonConnect;
    Button buttonClear;
    Label textResponse;

    @Override
    public void start(Stage primaryStage) {

        labelSys = new Label();
        welcomeMsg = new TextField();
        labelAddress = new Label("IP Address");
        textAddress = new TextField();
        labelPort = new Label("Port");
        textPort = new TextField();
        buttonConnect = new Button("Connect");
        buttonClear = new Button("Clear");
        textResponse = new Label();

        labelSys.setText(
                System.getProperty("os.arch") + "/"
                + System.getProperty("os.name"));

        HBox buttonbox = new HBox();
        buttonbox.setSpacing(5);
        HBox.setHgrow(buttonConnect, Priority.ALWAYS);
        HBox.setHgrow(buttonClear, Priority.ALWAYS);
        buttonConnect.setMaxWidth(Double.MAX_VALUE);
        buttonClear.setMaxWidth(Double.MAX_VALUE);
        buttonbox.getChildren().addAll(buttonConnect, buttonClear);

        buttonConnect.setOnAction(buttonConnectEventHandler);
        
        buttonClear.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                textResponse.setText("");
            }
        });

        VBox mainLayout = new VBox();
        mainLayout.setPadding(new Insets(5, 5, 5, 5));
        mainLayout.setSpacing(5);
        mainLayout.getChildren().addAll(labelSys, welcomeMsg,
                labelAddress, textAddress, labelPort, textPort,
                buttonbox, textResponse);

        StackPane root = new StackPane();
        root.getChildren().add(mainLayout);

        Scene scene = new Scene(root, 300, 400);

        String myPID = ManagementFactory.getRuntimeMXBean().getName();
        primaryStage.setTitle(myPID);
        primaryStage.setScene(scene);
        primaryStage.show();
        
        //Prepare and send preset setting
        String presetMsg = "Hello from " + myPID;
        welcomeMsg.setText(presetMsg);
        textAddress.setText("192.168.1.103");
        textPort.setText("8080");
        RunnableClient presetClient
            = new RunnableClient(textAddress.getText(), 
                    Integer.parseInt(textPort.getText()),
                    presetMsg);
            
        new Thread(presetClient).start();
        //---
    }

    public static void main(String[] args) {
        launch(args);
    }

    EventHandler<ActionEvent> buttonConnectEventHandler
        = new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            String tMsg = welcomeMsg.getText();
            if (tMsg.equals("")) {
                tMsg = null;
            }

            RunnableClient runnableClient
                = new RunnableClient(textAddress.getText(), 
                    Integer.parseInt(textPort.getText()),
                    tMsg);
            
            new Thread(runnableClient).start();
        }
    };

    class RunnableClient implements Runnable{
        
        String dstAddress;
        int dstPort;
        String response = "";
        String msgToServer;
        
        public RunnableClient(String addr, int port, String msgTo) {
            dstAddress = addr;
            dstPort = port;
            msgToServer = msgTo;
        }

        @Override
        public void run() {
            Socket socket = null;
            DataOutputStream dataOutputStream = null;
            DataInputStream dataInputStream = null;

            try {
                socket = new Socket(dstAddress, dstPort);
                dataOutputStream = new DataOutputStream(
                    socket.getOutputStream());
                dataInputStream = new DataInputStream(socket.getInputStream());

                if(msgToServer != null){
                    dataOutputStream.writeUTF(msgToServer);
                }
    
                response = dataInputStream.readUTF();
   

            } catch (IOException ex) { 
                Logger.getLogger(JavaFX_Client.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                
                Platform.runLater(new Runnable(){

                    @Override
                    public void run() {
                        textResponse.setText(response);
                    }
                    
                });
                
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Client.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                
                if (dataOutputStream != null) {
                    try {
                        dataOutputStream.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Client.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                
                if (dataInputStream != null) {
                    try {
                        dataInputStream.close();
                    } catch (IOException ex) {
                        Logger.getLogger(JavaFX_Client.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                
            }
        }
        
    }

}

Download.

For testing, you have to create a shell script and make it executable. to start 30 JavaFX_Client application.



No comments: