0

我一直在使用一个简单的 Eclipse 插件来创建可视状态机,称为statecharts,它也使用 Java 代码来工作。我的总体目标是让两个状态机通过套接字相互通信并交换数据并在此基础上进行转换,例如客户端-服务器通信。一开始我使用的是简单的同步客户端-服务器代码,但显然使用同步方法无济于事;正确的方法是不断地从队列中轮询数据。我现在正在尝试使用Java NIO这似乎很有希望,但不幸的是第一次尝试没有成功。似乎某处有一个繁忙的循环,不允许接收到的值触发更改。

代码非常简单:我首先尝试连接到服务器(有效),发送数据(有效),并尝试在每个周期从输入缓冲区中读取数据,以此作为接收数据的一种方式,如图所示。到目前为止的逻辑是有道理的。我将接收到的数据设置为也位于转换表达式中的变量。所以基本上只要它设置为真,我就应该转换到下一个状态。但它不起作用。

有人可以帮我解决这个问题吗?我已经看到有像NettyNaga这样的异步 API,如果这是一种补救措施,它们可能会使事情变得更容易。

这是状态机的视觉方案: 在此处输入图像描述

这是客户端的代码:

package test;

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

public class EchoClient2 {
    String serverHostname = new String("127.0.0.1");
    BufferedReader stdIn;
    Socket echoSocket = null;
    PrintWriter out = null;
    BufferedReader in = null;

    public void open(){
        System.out.println("Attemping to connect to host " + serverHostname
                + " on port 5555.");
        try {
            echoSocket = new Socket(serverHostname, 5555);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(
                    echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: " + serverHostname);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for " + "the connection to: "
                    + serverHostname);
        }
    }

    public void send(){
        String userInput = "1";
        out.println(userInput);
    }

    public String receive(){
        String result = "";
        try {
            result = in.readLine();
            if(result==null)
                return "0";
        } catch (IOException e) {
        }
        return result;
    }
}

这是服务器的代码:

package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer extends Thread {
    protected Socket clientSocket;

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(5555);
            System.out.println("Connection Socket Created");
            try {
                while (true) {
                    System.out.println("Waiting for Connection");
                    new EchoServer(serverSocket.accept());
                }
            } catch (IOException e) {
                System.err.println("Accept failed.");
                System.exit(1);
            }
        } catch (IOException e) {
            System.err.println("Could not listen on port: 5555.");
            System.exit(1);
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                System.err.println("Could not close port: 5555.");
                System.exit(1);
            }
        }
    }

    private EchoServer(Socket clientSoc) {
        clientSocket = clientSoc;
        start();
    }

    public void run() {
        System.out.println("New Communication Thread Started");

        try {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),
                    true);
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    clientSocket.getInputStream()));

            String inputLine;

            while ((inputLine = in.readLine()) != null) {
                System.out.println("Server: " + inputLine);
                out.println(inputLine);

                if (inputLine.equals("Bye."))
                    break;
            }

            out.close();
            in.close();
            clientSocket.close();
        } catch (IOException e) {
            System.err.println("Problem with Communication Server");
            System.exit(1);
        }
    }
}

这里是 Eclipse 项目文件夹如果这可能更容易,您可以简单地导入它。

4

1 回答 1

1

您打算做的事情背后的数学称为 PI 微积分。这不是唯一的方法,但它是一个很好的起点。

基本上,您将建模的是一种关系,其中两台机器可以进入相关状态,并且在发生共享条件(通常是传递消息)之前不会继续。

这意味着您必须将两个状态机都放在不同的线程上。尝试使用公共事件队列对机器进行排序,如果排序不稳定,这可能会变得非常有问题(如果排序不是对某个问题的补充,问题可能会更大)。

通常共享的信息是简化的。例如,许多系统使用“邮箱”类型的传递机制,其中一个状态机将消息传递到另一个的入站邮箱。然后传递状态机阻塞,直到消息清除邮箱。如果您以正确的方式将其形式化,您就可以有效地创建类似 Actor 的解决方案。如果您决定这是您可能希望继续进行的方式,那么尽早在邮箱中烘焙将允许您稍后用持久消息传递系统替换该类。

至于实际上使它与两个独立的进程一起工作,我最喜欢的方法是让第三个进程启动这两个进程,其中启动进程还创建任何需要的通信渠道。然后,该启动过程可以设置它的两个孩子的可配置元素。以这种方式做事可能需要一些关于如何创建“系统服务”(也就是没有 ttys 的程序)的知识,但这是很好的知识。此外,我更喜欢 JMS API 和众多实现之一。

如果你真的想“自己动手”,那么我会从一个比完整的 NIO 解决方案少一点的东西开始。请记住,当您具有某些通信模式时,NIO 可以很好地扩展,但是单个状态机阻塞所需的输入(或所需的交付确认)不需要跨线程池扩展或等待复杂的事件回调。当然,其他人可能会有不同的看法,但某些工作流程使用可扩展性较低的解决方案确实可以更快地进行基准测试(我认为这可能是一项工作,可扩展性不会给你带来太多好处,除非你真的代理数十个或数百个状态机在同一进程中)。

于 2015-10-26T16:46:57.880 回答