Связь между двумя конечными автоматами в Java
Я использовал простой плагин eclipse для создания визуальных конечных автоматов, называемых диаграммами состояний, который также использует код Java для работы. Моя общая цель - сделать так, чтобы два конечных автомата общались друг с другом через сокеты, обменивались данными и на их основе делали переходы, такие как связь клиент-сервер. В начале я использовал простой синхронный клиент-серверный код, но, видимо, использование синхронного подхода не может помочь; правильный способ - постоянно опрашивать данные из очереди. Я сейчас пытаюсь использовать Java NIO
что кажется многообещающим, но, к сожалению, не удалось с первой попытки. Кажется, что где-то есть занятый цикл, который не позволяет полученному значению вызвать изменение.
Код довольно прост: сначала я пытаюсь подключиться к серверу (это работает), отправлять данные (это работает) и пытаться читать из входного буфера каждый цикл как способ получения данных, как вы можете видеть на рисунке. Логика до сих пор имеет смысл. Я установил полученные данные в переменную, которая также находится в выражении перехода. Так что, в основном, когда бы это ни было установлено, я должен переходить в следующее состояние Но это не работает.
Может кто-нибудь помочь мне решить эту проблему? Я видел, что есть асинхронные API, такие как Netty и Naga, которые могут упростить ситуацию, если это будет исправление.
Вот визуальная схема конечного автомата:
Вот код для клиента:
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, которую вы можете просто импортировать, если это будет проще.
1 ответ
Математика, стоящая за тем, что вы намереваетесь сделать, называется PI Calculus. Это не единственный подход, но это отличное место для начала.
По сути, вы будете моделировать отношения, в которых две машины могут входить в связанные состояния и не переходить в состояние, пока не произойдет общее состояние (которое обычно является передачей сообщения).
Это означает, что вы должны иметь оба своих конечных автомата в отдельных потоках. Попытка использовать общую очередь событий упорядочивает машины, что может стать очень проблематичным, если упорядочение не является стабильным (и может быть еще более проблематичным, если упорядочение не дополняет определенную проблему).
Часто общее сообщение упрощается. Например, многие системы используют механизм доставки типа "почтовый ящик", когда один конечный автомат доставляет сообщение во входящий почтовый ящик другого. Затем конечный автомат доставки блокируется, пока сообщение не очистит почтовый ящик. Если вы правильно оформите это, вы фактически создадите решение, подобное актеру. Если вы решите, что именно таким способом вы могли бы продолжить, выпечка в почтовом ящике на раннем этапе позволит вам позже заменить класс постоянной системой доставки сообщений.
Что касается фактической работы с двумя независимыми процессами, мой любимый подход заключается в том, чтобы третий процесс запускал два, где процесс запуска также создает любые необходимые каналы связи. Этот процесс запуска может затем установить настраиваемые элементы его двух дочерних элементов. Чтобы сделать это таким образом, может потребоваться немного знаний о том, как создавать "системные службы", то есть программы без ttys, но это хорошее знание. Также я предпочитаю JMS API и одну из многих реализаций.
Если вы действительно хотите "сделать что-то свое", я бы начал с чего-то чуть менее полного решения NIO. Помните, что NIO хорошо масштабируется, когда у вас есть определенные шаблоны связи, но один конечный автомат, блокирующий необходимый ввод (или необходимое подтверждение доставки), не должен масштабироваться по пулам потоков или ожидать сложных обратных вызовов. Конечно, другие могут расходиться во мнениях, но некоторые рабочие процессы быстрее справляются с сравнением с менее масштабируемым решением (и я думаю, что это может быть работа, где масштабируемость не принесет вам большой пользы, если вы действительно не используете прокси для десятков или сотни конечных автоматов в одном процессе).