【问题标题】:JavaFX Socket HandlingJavaFX 套接字处理
【发布时间】:2014-01-23 04:15:34
【问题描述】:

尝试说明情况:

我创建了一个 SocketManager 类。这扩展了 JavaFX 服务,旨在创建新线程(我已扩展并称为 SocketListener 的 JavaFX 任务)来处理特定的套接字连接。

在 SocketListener 中,我有一个循环侦听套接字上的输入(并回复客户端)。

问题:SocketListener 中的循环被阻塞(显然)。这意味着我永远不会到达 Socket Listener(扩展 JavaFX 任务)的调用方法中的“返回”。 我很好奇要实现什么模式,这样我就可以从 SocketListener 返回一个值,而不必中断与客户端保持连接的循环。想到的一个想法是创建一个自定义事件以在每条消息通过套接字时触发,但我想确保我实现的任何东西不仅“工作”而且是最佳的。

这是我引用的两个类的代码:

import javafx.concurrent.Service;
import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * The primary class for listening for and issuing communication over sockets in VDTS
 * applications.
 * WARNING: This class is blocking, and should be run in a separate thread.
 */
public class SocketManager extends Service<SocketMessage> {

    /**
     * The port that SocketManager instance will listen on.
     */
    int listenPort;

    /**
     * The ServerSocket instance used by SocketManager for receiving socket connections.
     */
    ServerSocket server;

    /**
     * Constructs a SocketManager and begins listening on the given port.
     * @param listenPort The port that the SocketManager instance will listen on.
     */
    public SocketManager(int listenPort){
        this.listenPort = listenPort;
        try {
            this.server = new ServerSocket(listenPort);
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
            //TODO
        }
    }

    /**
     * Returns the port currently being listened to by SocketManager instance.
     * @return the port being listened to.
     */
    public int getListenPort() {
        return listenPort;
    }

    /**
     * Sets a new port for the SocketManager instance to listen to. This will destroy
     * the current ServerSocket, ending any communication via the old instance. Use
     * with caution.
     * @param listenPort The new port this SocketManager instance will listen on.
     */
    public void setListenPort(int listenPort) {
        this.listenPort = listenPort;
    }

    @Override
    protected Task createTask() {
        String returnedMessage;
        Socket client;
        SocketListener messenger;
        while(true){
            try {
                client = null;
                client = server.accept();
                messenger = new SocketListener(client);
                new Thread(messenger).start();
            } catch (IOException ex) {
                System.out.println(ex.getMessage());
                //TODO
            }
        }
    }
}

还有一个

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

/**
 * An extension on javafx.concurrency.Task.
 * SocketListener is responsible for retrieving a message (string) from a client over
 * the socket and returning this string (Usually to a SocketManager).
 */
public class SocketListener extends javafx.concurrent.Task<String> {

    /**
     * Client socket which is referenced for persistent communication.
     */
    private final Socket client;

    /**
     * PrintWriter used to write to client.
     */
    private PrintWriter clientOut;

    //TODO This is a temporary property for testing, remove it;
    private int messageCount = 0;

    /**
     * Constructs a SocketListener given a client (Socket)
     * @param client
     */
    public SocketListener(Socket client){
        this.client = client;
        try {
            this.clientOut = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Listens for and returns the value of the message received over socket.
     * @return The message received from the client.
     * @throws Exception
     */
    @Override
    protected String call() throws Exception {
        String nextLine = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        //TODO remove in production should not require highfive
        clientOut.println("Just saying hi.");
        while ((nextLine = in.readLine()) != null) {
            this.messageCount++;
            clientOut.println("You've messaged me " + this.messageCount + " times.");
        }
        return "Socket closed.";
    }
}

【问题讨论】:

    标签: sockets javafx


    【解决方案1】:

    您为什么不尝试使用其他选项?通过套接字输入和输出数据的标准库效率不高(java.net)。您可以尝试使用 Java NIO library,它将为您的代码提供更大的灵活性。

    NIO 为您提供了启动连接的能力,因此您可以选择代码是否会阻止您的连接,但您必须使用循环。在循环内部你可以观察到两点之间进行了何种类型的通信,然后按照你的意愿处理通信。然而,如果有更多创意,您可以在服务器端使用 executors 服务,从而使其他线程只需要处理通信,而您只需要一个线程来负责通信或交换数据。

    JavaFX 是一种技术,它为开发丰富的应用程序带来了出色的工具。由于它被认为是一种不同类型的技术并使用另一种方法,JavaFX(如 Swing)带来了一些工具来保持您的代码线程安全,这正是 TaskService 以及除其他外,如 @987654324来自Platform的@方法。

    所以你所能做的就是混合使用这两种技术,NIO 和 JavaFX。当您在应用程序中进行一些处理时,无论是客户端还是服务器,在JavaFX Application Thread. 以外的线程上执行处理当您必须在 JavaFX 应用程序中显示数据时,只需使用 JavaFX 应用程序线程即可。世界上没有比这更简单的事情了。 ;)

    如果您不喜欢 NIO 技术,您仍然可以尝试其他“更好”开发的技术,例如Apache MINA projectNETTY framework。这些项目为使用 NIO 的任何人开发了更实用的解决方案,它们非常有用,因为您不必重新发明轮子。

    如果您仍有疑问,请提出。祝你好运。 (:

    【讨论】:

    • 感谢您的深思熟虑的答复。我确实有一个问题,我希望这不是太大的负担。我很好奇在我的套接字处理中你建议我“放弃”给 JavaFX 的哪一点。在我的预期架构中,我将有两个阻塞循环(我将把它们放在线程中)。一个循环不断侦听到套接字的新连接并为每个连接创建新线程,一个循环处理这些单独的连接。现在我我正在编写一个希望在许多应用程序中使用的包,所以我希望保持逻辑相当抽象。
    • @jacheson: "...为每个连接创建新线程..." - 啊!请不要为每个传入连接创建一个新线程!这最终会破坏程序的性能,导致它最终变得完全没有响应。 Java 线程被设计成异步处理,每个线程对应一个处理器内核。通过使用 NIO 库,您最终会在高级应用程序中获得出色的性能(即使使用单线程)。我再次询问您是否考虑学习 NIO。这样做你会做得很好(更不用说它很容易理解)。
    • @jacheson 如果您想考虑阅读易懂且快速的阅读材料,我建议您查找 O'Reilly 的《Java NIO》一书。您甚至可以通过Amazon website 为您订购一个。
    • @jacheson 您还将发现简单但重要的示例,这些示例将使您了解如何创建更好的应用程序。我再次问你,考虑阅读 NIO。您在程序中使用 2 个循环的方法看起来确实不是一个好的选择。
    • 感谢您的建议。我有朋友/同事在 Netty 方面有经验,所以可以选择该解决方案,因为我可以快速获得建议/帮助。这有意义吗?这个解决方案是否适合我的 Java NIO 问题?
    猜你喜欢
    • 2018-11-20
    • 2019-03-18
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 2019-01-16
    • 2016-05-14
    相关资源
    最近更新 更多