Search code examples
javasocketsjavafxserversocket

Get Real-time data from server socket and Display it in javafx


Hello I need to get Some data from a server socket i created and display it in a javafx application fx application ie the display refresh the data every 250ms, and server sends data every 2 seconds

My code/plan have mainly 3 Parts
1. The server generates the data and sends it to the port every 2 sec
2. The Clint code gets the data from the server and updates its global variables
3. Every 250ms Schedule executioner reach out to the global varibles in clint and update the text fields
// Sadly this doesn't seems to work
i always starts server then clint then runs the application

so the Codes i written are as follows

Server Code

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
public class Server {
    public static void main(String[] args) throws IOException, InterruptedException {
        Socket socket;
        try (ServerSocket serverSocket = new ServerSocket(5555)) {
            System.out.println("A");
            socket = serverSocket.accept();
            System.out.println("B");
            if(socket.isConnected())System.out.println("Connected");
            DataOutputStream dout=new DataOutputStream(socket.getOutputStream());
            while (socket.isConnected()) {
                String T=DataStructureMaker();
                dout.writeUTF(T);
                System.out.println(T);
                TimeUnit.SECONDS.sleep(2);
                dout.flush();
            }
        }
        socket.close();
    } 
    public static String DataStructureMaker()
    {
    float RV_Phase=0,RI_Phase=0,RI_Grid=0,RV_Grid=0;String s="";
        DecimalFormat df = new DecimalFormat("#.00");
        s="";
        RV_Phase=Float.parseFloat(df.format((Math.random()*10)));
        s=s+Float.toString(RV_Phase)+"#";
        RI_Phase=Float.parseFloat(df.format((Math.random()*10)));
        s=s+Float.toString(RI_Phase)+"#";
        RI_Grid=Float.parseFloat(df.format((Math.random()*10)));
        s=s+Float.toString(RI_Grid)+"#";
        RV_Grid=Float.parseFloat(df.format((Math.random()*10)));
        s=s+Float.toString(RV_Grid)+"#";
        return s;
    }
}

The clint code is

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
public class Clint {
    public static String RV_Grid;
    public static String RI_Grid;
    public static String RI_Phase;
    public static String RV_Phase;
    public static void main(String[] args) throws IOException, InterruptedException {
        Socket s=new Socket("localhost",5555);
        String S;
        DataInputStream dIn=new DataInputStream(s.getInputStream());
        while (s.isConnected()) {            
            S=dIn.readUTF();
            setData(S);
        }
    }
    public static void setData(String S)  // Decryt data and set global values
    {
        char[] A=S.toCharArray();
        int HC=0;
        String R="";
        if(A.length>2)
        for(char x:A)
        {
            if(x=='#')
            {
                switch(HC)
                {
                    case 0:
                        HC++;
                        RV_Phase=R;
                        R="";
                        break;
                    case 1:
                        HC++;
                        RI_Phase=R;
                        R="";
                        break;
                    case 2:
                        HC++;
                        RI_Grid=R;
                        R="";
                        break;
                    case 3:
                        HC++;
                        RV_Grid=R;
                        R="";
                        break;
                }
            }else{
                R=R+x;
            }
        }
    }    
}

and Finally my fxml controller

import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
public class FXMLDocumentController implements Initializable {

    @FXML
    private TextField Text1;
    @FXML
    private TextField Text2;
    @FXML
    private TextField Text3;
    @FXML
    private TextField Text4;


    static ScheduledExecutorService scheduledExecutorService;
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO

        scheduledExecutorService =         Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            setData();

            }, 0, 250, TimeUnit.MILLISECONDS);
    }    
    public void setData()
    {
        Text1.setText(Clint.RI_Grid);
        Text2.setText(Clint.RI_Phase);
        Text3.setText(Clint.RV_Grid);
        Text4.setText(Clint.RV_Phase);
    }       
}

The Above code have All impotent data needed for my program Encryption, Decryption Send Receive and Display


Solution

  • The code following is aimed to serve two purposes:
    The one is to demonstrate an mre for the question asked.
    The other being a solution for dynamic update the GUI with data received by the client.
    The code is not aimed to show how to correctly implement the client and server.

    There are a few things to note about MRE:
    1. It should not represent your specific application but a focused specific problem you are trying to solve.
    2. It should be M: remove all what is not essential. Bare minimum.
    3. It should be R: reproduce the problem.
    4. It should be complete.Independent on data-base, files or other unavailable resources.
    5. It should be easy to use (copy-past).

    To dynamically update the GUI with data received by the client, I used a shared model, that it is updated by the client.
    Any change in the model ifs reflected in the GUI by using binding.

    For simplicity and ease of use I have the controller, the model, the server and the client all in one file FXMLDocumentController.java:

    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.URL;
    import java.text.DecimalFormat;
    import java.util.ResourceBundle;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    import javafx.beans.property.ReadOnlyStringWrapper;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.Button;
    import javafx.scene.control.TextField;
    
    public class FXMLDocumentController implements Initializable {
    
        private static int PORT_NUMBER = 5555;
        @FXML
        private TextField text1;
        @FXML
        Button stopButton, startButton;
    
        private ScheduledExecutorService scheduledExecutorService;
        private Model model;
        private Client client;
        private Server server;
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            startButton.setDisable(false);
            stopButton.setDisable(true);
            scheduledExecutorService = Executors.newScheduledThreadPool(2);
            model = new Model();
            text1.textProperty().bind(model.getTextProperty());
        }
    
        private void startServer(){
    
            try {
                server = new Server(PORT_NUMBER);
                server.start();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    
        private void startClient(){
    
            try {
                client = new Client(PORT_NUMBER, model);
                client.start();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    
        public void start(){
    
            scheduledExecutorService.submit(() -> startServer());
            scheduledExecutorService.submit(() -> startClient());
    
            startButton.setDisable(true);
            stopButton.setDisable(false);
        }
    
        public void stop(){
    
            client.stop();
            server.stop();
    
            scheduledExecutorService.shutdown();
            stopButton.setDisable(true);
        }
    }
    
    class Model {
    
        private final ReadOnlyStringWrapper textProperty;
    
        Model() {
            textProperty = new ReadOnlyStringWrapper();
        }
    
        synchronized void setText(String s){
            Platform.runLater(()->textProperty.set(s));
        }
    
        ReadOnlyStringWrapper getTextProperty(){
            return textProperty;
        }
    }
    
    class Server {
    
        private final int portNumber;
        private volatile boolean stop = false;
        private static long REFRESH_TIME = 2;
    
        Server(int portNumber) {
            this.portNumber = portNumber;
        }
    
        void start() throws IOException {
    
            Socket socket;
            try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
                socket = serverSocket.accept();
                DataOutputStream dout=new DataOutputStream(socket.getOutputStream());
                while (socket.isConnected() && ! stop) {
                    dout.writeUTF(randomText());
                    try {
                        TimeUnit.SECONDS.sleep(REFRESH_TIME);
                    } catch (InterruptedException ex) {
                        break;
                    }
                    dout.flush();
                }
            }
        }
    
        private String randomText()
        {
            DecimalFormat df = new DecimalFormat("#.00");
            StringBuilder sb = new StringBuilder(df.format(Math.random()*10));
            sb.append("#")
            .append(df.format(Math.random()*10)) ;
            return sb.toString();
        }
    
        void stop(){
            stop = true;
        }
    }
    
    class Client {
    
        private final int portNumber;
        private final Model model;
        private volatile boolean stop = false;
    
        Client(int portNumber,  Model model) {
            this.portNumber = portNumber;
            this.model = model;
        }
    
        void start() throws IOException {
            Socket socket = new Socket("localhost",portNumber);
            DataInputStream dIn=new DataInputStream(socket.getInputStream());
            while (socket.isConnected() && ! stop) {
                model.setText(dIn.readUTF());
            }
            socket.close();
        }
    
        void stop(){
            stop = true;
        }
    }
    

    The controller is used by FXMLDocument.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.layout.GridPane?>
    <?import javafx.scene.layout.VBox?>
    
    <VBox alignment="CENTER" prefHeight="113.0" prefWidth="232.0" spacing="10" xmlns="http://javafx.com/javafx/10.0.1" 
    xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx_tests.FXMLDocumentController">
    
         <TextField fx:id="text1" alignment="CENTER" promptText="Press START " />
         <GridPane>
             <Button fx:id="startButton" maxWidth="Infinity" onAction="#start" text="START" GridPane.columnIndex="0" />
             <Button fx:id="stopButton" maxWidth="Infinity" onAction="#stop" text="STOP" GridPane.columnIndex="2" />
         </GridPane>
    </VBox>
    

    Test it with :

    import java.io.IOException;
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.scene.layout.Pane;
    import javafx.stage.Stage;
    
    public class FxmlTest extends Application {
    
        @Override
        public void start(Stage primaryStage) throws IOException {
    
            Pane root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(null);
        }
    }