简体   繁体   English

如何将对象从 inputStream 客户端返回到 Javafx Controller?

[英]How to return objects from inputStream Client to a Javafx Controller?

So basically I have a MainConstroller class that has methods for every button.所以基本上我有一个 MainController class ,每个按钮都有方法。 I have also a server multi-client application.我还有一个服务器多客户端应用程序。 In Client I have a method sendMessage that sends a String and an Object as a parameter to outputStreams to Server.在客户端中,我有一个方法 sendMessage,它发送一个字符串和一个 Object 作为 outputStreams 到服务器的参数。

In the same method I have 2 inputStream for message from server and an object.在相同的方法中,我有 2 个用于来自服务器的消息的 inputStream 和一个 object。 The problem is that this method runs on a Thread which implements run method and I cannot return the object.问题是这个方法在实现 run 方法的线程上运行,我无法返回 object。

I tried to create a static class that saves these, but the getters are null, when called in Controller class. I tried to create a static class that saves these, but the getters are null, when called in Controller class.

What is the best method to achieve this?实现这一目标的最佳方法是什么?

public void onSaveButton(javafx.event.ActionEvent actionEvent) throws Exception {


    Parent root = null;
    Boolean type = false;
    String message = null;


    if (adminCheckbox.isSelected()) {
        root = FXMLLoader.load(getClass().getResource("/fxml/admin.fxml"));
        type = true;
        message = "Admin";
    }

    if (competitorCheckbox.isSelected()) {
        root = FXMLLoader.load(getClass().getResource("/fxml/competitor.fxml"));
        message = "Competitor";
    }

    PersonEntity personEntity = new PersonEntity();
    personEntity.setIdTeam(Integer.parseInt(teamField.getText()));
    personEntity.setType(type);
    personEntity.setUsername(usernameField.getText());

    client.sendMessageToServer(message, personEntity);

    System.out.println(Utils.getMessage());}

And the Client method:和客户端方法:

 public void sendMessageToServer(String message, Object object) throws Exception {

    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Say something and the message will be sent to the server: ");

            //For receiving and sending data
            boolean isClose = false;

            while (!isClose) {
                try {

                    ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
                    ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());

                    if (message.equals("Bye")) {
                        isClose = true;
                    }

                    outputStream.writeObject(message);
                    outputStream.writeObject(object);

                    String messageFromServer = (String) inputStream.readObject();
                    //System.out.println(messageFromServer);

                    int index = messageFromServer.indexOf(' ');
                    String word = messageFromServer.substring(0, index);

                    if (messageFromServer.equals("Bye")) {
                        isClose = true;
                    }

                    if (!word.equals("LIST")) {
                        Object obj = (Object) inputStream.readObject();
                        Utils.setMessage(messageFromServer);
                        return;
                        //Utils.setObject(obj);
                        //System.out.println("IN FOR " + Utils.getMessage());

                    } else {
                        List<Object> list = (List<Object>) inputStream.readObject();
                        Utils.setMessage(messageFromServer);
                        Utils.setObjects(list);
                        return;

                    }

                } catch (IOException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

You can use a thread-safe ConcurrentLinkedQueue (static, of course).您可以使用线程安全的 ConcurrentLinkedQueue(当然是静态的)。 Utils.setObjects would add each element to the queue. Utils.setObjects会将每个元素添加到队列中。 On the client side, you would occasionally poll for new items on the queue to be displayed in the UI.在客户端,您偶尔会轮询队列中要在 UI 中显示的新项目。

Reference documentation: https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html参考文档: https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html

I don't know what your server is, nor the rest of your app.我不知道您的服务器是什么,也不知道您的应用程序的 rest 是什么。 I know you are using raw TCP sockets for communication using a custom protocol.我知道您正在使用原始 TCP sockets 使用自定义协议进行通信。

What I did was take code for a similar example from the Java tutorials.我所做的是从 Java 教程中获取类似示例的代码。 The code is the KnockKnock client server code from: Writing the Server Side of a Socket .该代码是 KnockKnock 客户端服务器代码,来自: Writing the Server Side of a Socket The basic server code is unchanged, I just replaced the console-based client with a JavaFX UI.基本的服务器代码没有改变,我只是用 JavaFX UI 替换了基于控制台的客户端。

Unchanged server code:未更改的服务器代码:

Replaced console client:替换控制台客户端:

I provide two different client implementations;我提供了两种不同的客户端实现; one makes synchronous client calls on the JavaFX application thread, the other makes asynchronous client calls using a JavaFX task.一个在 JavaFX 应用程序线程上进行同步客户端调用,另一个使用 JavaFX 任务进行异步客户端调用。

The current asynchronous task-based solution is not robust enough for a full-scale production system as it is technically possible for it to lose messages and it doesn't robustly match request and response messages.当前基于异步任务的解决方案对于完整的生产系统来说不够健壮,因为它在技术上可能会丢失消息并且它不能健壮地匹配请求和响应消息。 But for a demo like this it is OK.但是对于这样的演示,它是可以的。

For simple execution, the UI apps both launch a local server on a pre-defined port when they are run, but you could remove the code to launch the server from the UI apps and run the server from the command line if you want.为了简单的执行,UI 应用程序在运行时都会在预定义的端口上启动本地服务器,但您可以删除从 UI 应用程序启动服务器的代码,并根据需要从命令行运行服务器。

敲敲

KnockKnockSyncClient.java KnockKnockSyncClient.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class KnockKnockSyncClient {
    private Socket socket;
    private PrintWriter out;
    private BufferedReader in;

    public String connect(String host, int port) {
        try {
            socket = new Socket(host, port);

            out = new PrintWriter(
                    socket.getOutputStream(),
                    true
            );

            in = new BufferedReader(
                    new InputStreamReader(
                            socket.getInputStream()
                    )
            );

            return in.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public String sendMessage(String request) {
        String response = null;

        try {
            out.println(request);
            response = in.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return response;
    }

    public void disconnect() {
        try {
            if (socket != null) {
                socket.close();
                socket = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

KnockKnockSyncApp KnockKnockSyncApp

import javafx.animation.FadeTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class KnockKnockSyncApp extends Application {
    private static final String HOST = "localhost";
    private static final int PORT = 8809;

    public static final String QUIT_RESPONSE = "Bye.";

    private ExecutorService serverExecutor;
    private KnockKnockSyncClient client;

    private static final String CSS = """
            data:text/css,
            .root {
                -fx-font-size: 20;
            }
            .list-cell {
                -fx-alignment: baseline-right;
                -fx-text-fill: purple;
            }
            .list-cell:odd {
                -fx-alignment: baseline-left;
                -fx-text-fill: darkgreen;
            }
            """;

    @Override
    public void init() {
        serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
        serverExecutor.submit(
                () -> {
                    try {
                        KKMultiServer.main(new String[]{ PORT + "" });
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        );

        client = new KnockKnockSyncClient();
    }

    @Override
    public void start(Stage stage) {
        ListView<String> messageView = new ListView<>();

        TextField inputField = new TextField();
        inputField.setPromptText("Enter a message for the server.");

        inputField.setOnAction(event -> {
            String request = inputField.getText();
            messageView.getItems().add(request);

            String response = client.sendMessage(request);
            messageView.getItems().add(response);

            messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));

            inputField.clear();

            if (QUIT_RESPONSE.equals(response)) {
                closeApp(inputField.getScene());
            }
        });

        VBox layout = new VBox(10,
                messageView,
                inputField
        );
        layout.setPadding(new Insets(10));
        layout.setPrefWidth(600);

        Scene scene = new Scene(layout);
        scene.getStylesheets().add(CSS);

        stage.setScene(scene);
        stage.show();

        inputField.requestFocus();

        String connectResponse = client.connect(HOST, PORT);
        if (connectResponse != null) {
            messageView.getItems().add(connectResponse);
        }
    }

    private void closeApp(Scene scene) {
        Parent root = scene.getRoot();
        root.setDisable(true);
        FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
        fade.setToValue(0);
        fade.setOnFinished(e -> Platform.exit());
        fade.play();
    }

    @Override
    public void stop() {
        client.disconnect();
        serverExecutor.shutdown();
    }

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

KnockKnockAsyncClient.java KnockKnockAsyncClient.java

import javafx.concurrent.Task;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

public class KnockKnockAsyncClient extends Task<Void> {
    private final String host;
    private final int port;

    private final BlockingQueue<String> messageQueue = new LinkedBlockingDeque<>();

    public KnockKnockAsyncClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Override
    protected Void call() {
        try (
                Socket kkSocket = new Socket(host, port);
                PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(kkSocket.getInputStream()));
        ) {
            String fromServer;
            String fromUser;

            while ((fromServer = in.readLine()) != null) {
                // this is not a completely robust implementation because updateMessage
                // can coalesce responses and there is no matching in send message calls
                // to responses.  A more robust implementation may allow matching of
                // requests and responses, perhaps via a message id.  It could also
                // replace the updateMessage call with storage of results in a collection
                // (e.g. an observable list) with thread safe notifications of response
                // arrivals, e.g. through CompleteableFutures and/or Platform.runLater calls.
                updateMessage(fromServer);
                System.out.println("Server: " + fromServer);
                if (fromServer.equals("Bye."))
                    break;

                fromUser = messageQueue.take();
                System.out.println("Client: " + fromUser);
                out.println(fromUser);
            }
        } catch (InterruptedException e) {
            if (isCancelled()) {
                updateMessage("Cancelled");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public void sendMessage(String request) {
        messageQueue.add(request);
    }
}

KnockKnockAsyncApp.java KnockKnockAsyncApp.java

import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class KnockKnockAsyncApp extends Application {
    private static final String HOST = "localhost";
    private static final int PORT = 8809;

    public static final String QUIT_RESPONSE = "Bye.";

    private ExecutorService serverExecutor;
    private ExecutorService clientExecutor;

    private static final String CSS = """
            data:text/css,
            .root {
                -fx-font-size: 20;
            }
            .list-cell {
                -fx-alignment: baseline-right;
                -fx-text-fill: purple;
            }
            .list-cell:odd {
                -fx-alignment: baseline-left;
                -fx-text-fill: darkgreen;
            }
            """;

    @Override
    public void init() {
        serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
        serverExecutor.submit(
                () -> {
                    try {
                        KKMultiServer.main(new String[]{ PORT + "" });
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        );

        clientExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
            final AtomicInteger threadNum = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"KKClient-" + threadNum.getAndAdd(1));
            }
        });
    }

    @Override
    public void start(Stage stage) {
        ListView<String> messageView = new ListView<>();

        KnockKnockAsyncClient client = new KnockKnockAsyncClient(HOST, PORT);
        // monitor and action responses from the server.
        client.messageProperty().addListener((observable, oldValue, response) -> {
            if (response != null) {
                logMessage(messageView, response);
            }

            if (QUIT_RESPONSE.equals(response)) {
                closeApp(messageView.getScene());
            }
        });

        TextField inputField = new TextField();
        inputField.setPromptText("Enter a message for the server.");

        inputField.setOnAction(event -> {
            String request = inputField.getText();
            logMessage(messageView, request);

            client.sendMessage(request);

            inputField.clear();
        });

        VBox layout = new VBox(10,
                messageView,
                inputField
        );
        layout.setPadding(new Insets(10));
        layout.setPrefWidth(600);

        Scene scene = new Scene(layout);
        scene.getStylesheets().add(CSS);

        stage.setScene(scene);
        stage.show();

        inputField.requestFocus();

        clientExecutor.submit(client);
    }

    private void logMessage(ListView<String> messageView, String request) {
        messageView.getItems().add(request);
        messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));
    }

    private void closeApp(Scene scene) {
        Parent root = scene.getRoot();
        root.setDisable(true);
        FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
        fade.setToValue(0);
        fade.setOnFinished(e -> Platform.exit());
        fade.play();
    }

    @Override
    public void stop() {
        clientExecutor.shutdown();
        serverExecutor.shutdown();
    }

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM