我正在从编写Java Swing应用程序转换到JavaFX以编写基于Java的现代GUI应用程序。在GUI中与JavaFX网络服务交互
我想知道创建基于网络的可重用线程服务的最佳方法。我编写网络服务的方式是使用一个控制器类(通过Net-beans GUI从FXML生成)。我通过名为'transmitter
'的私人服务成员将线程逻辑放在这里,并通过Start/Stop按钮的事件回调连接了启动/停止逻辑。
基于网络的线程被实现为javafx服务 - 我这样做是因为我想在目标地址更改时重新启动服务/线程。这似乎是替代独立任务的推荐方法。
网络服务现在非常简单,只需使用一些GUI小部件即可将数据包配置为每秒传输一次主机/端口。我只需要在主机/端口小部件发生变化时重新启动服务,但是如果网络服务正在运行,我想在不中断/重新启动DatagramSocket的情况下修改数据包。我有疑问并需要一些指导的地方是:
- 在基于FXML的应用程序中线程化网络线程的建议方法是什么?一个例子将不胜感激。
- 如何安全地将来自GUI小部件的更改(通过其执行回调的 动作)与正在运行的服务类进行通信?
下面显示的是我的控制器类的最相关的部分:
/**
* FXML Controller class
*
* @author johnc
*/
public class OpMessageServerController implements Initializable {
@FXML
private Text mCurrentDateTimeText;
@FXML
private Label mApplicationStatus;
@FXML
private ComboBox<DiscreteStatus> mPofDS;
@FXML
private ComboBox<PhaseOfFlightFMS> mPofFMS;
@FXML
private ComboBox<DiscreteStatus> mTailNumberDS;
@FXML
private ComboBox<DiscreteStatus> mConfigTableDS;
@FXML
private ComboBox<DiscreteStatus> mDateTimeDS;
@FXML
private TextField mEpicPN;
@FXML
private TextField mConfigTablePNHash;
@FXML
private TextField mTailNumber;
@FXML
private ComboBox<DiscreteStatus> mTopLevelPNDS;
@FXML
private Button mStartStopButton;
@FXML
private ComboBox<String> mDLMUHostSpec;
@FXML
private CheckBox connectionStatusC1;
@FXML
private CheckBox wsuConnectionStatus;
@FXML
private CheckBox connectionStatusC4;
@FXML
private CheckBox connectionStatusC3;
@FXML
private CheckBox connectionStatusC2;
@FXML
private CheckBox dlmuwConnectionStatus;
private Service<Void> transmitter;
/**
* Initializes the controller class.
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
mPofDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
mPofDS.getSelectionModel().selectFirst();
mPofFMS.setItems(FXCollections.observableArrayList(PhaseOfFlightFMS.values()));
mPofFMS.getSelectionModel().selectFirst();
mTailNumberDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
mTailNumberDS.getSelectionModel().selectFirst();
mConfigTableDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
mConfigTableDS.getSelectionModel().selectFirst();
mDateTimeDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
mDateTimeDS.getSelectionModel().selectFirst();
mTopLevelPNDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
mTopLevelPNDS.getSelectionModel().selectFirst();
// mDLMUHostSpec.setItems(FXCollections.observableArrayList(
// FXCollections.observableArrayList("localhost:1234", "192.168.200.2:1234")));
// add event handler here to update the current date/time label
// this should also update the transmit datastructure
final Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(1), (ActionEvent event) -> {
LocalDateTime currentDateTime = LocalDateTime.now();
mCurrentDateTimeText.setText(currentDateTime.format(
DateTimeFormatter.ofPattern("kk:mm:ss uuuu")));
}));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
// create a service.
transmitter = new Service() {
@Override
protected Task createTask() {
return new Task<Void>() {
@Override
protected Void call() throws InterruptedException {
updateMessage("Running...");
updateProgress(0, 10);
DatagramSocket sock = null;
while (!isCancelled()) {
try {
if (sock == null) {
DatagramSocket sock = new DatagramSocket();
}
} catch (SocketException ex) {
Logger.getLogger(OpMessageServerController.class.getName()).log(Level.SEVERE, null, ex);
}
//Block the thread for a short time, but be sure
//to check the InterruptedException for cancellation
OpSupportMessage opSupportMessage = new OpSupportMessage(
DiscreteStatus.NormalOperation,
PhaseOfFlightFMS.Cruise,
DiscreteStatus.NormalOperation,
"TAILNUM",
DiscreteStatus.NormalOperation);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String[] specParts = mDLMUHostSpec.getValue().split(":");
if (specParts.length == 2) {
try {
opSupportMessage.write(bos);
byte[] buff = bos.toByteArray();
DatagramPacket packet = new DatagramPacket(
buff, buff.length, InetAddress.getByName(
specParts[0]), Integer.parseInt(specParts[1]));
mSocket.send(packet);
Thread.sleep(1000);
} catch (IOException ex) {
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
}
}
}
updateMessage("Cancelled");
return null;
}
@Override
protected void succeeded() {
System.out.println("Scanning completed.");
}
@Override
protected void failed() {
System.out.println("Scanning failed.");
}
@Override
protected void running() {
System.out.println("Scanning started.");
}
@Override
protected void cancelled() {
System.out.println("Scanning cancelled.");
}
private void DatagramSocket() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
}
};
mApplicationStatus.textProperty().bind(transmitter.messageProperty());
};
@FXML
private void startStopButtonAction(ActionEvent event) {
if (!transmitter.isRunning()) {
transmitter.reset();
transmitter.start();
}
}
…
}
当然,您并不是说您的GUI应用程序代表网络上的客户端提供网络服务?你想在服务器上使用什么GUI? – scottb
@scottb不,这是一个简单的gui客户端应用程序,它包含一个UDP发送线程,我只需要能够控制出去的数据包和这些数据包的内容。其中一个GUI控件“mDLMUHostSpec”包含一个可编辑的IP /端口,其他控件都是配置数据包的内容,如果IP /端口发生变化,则需要停止并重新启动现有服务。如果GUI中的其他参数发生变化(这会影响客户端服务传输的数据报(不会与服务器混淆,因为我认为您可能已经解释过)),但这些参数需要安全更新。 – johnco3
我使用基于Web套接字的通信[javafx-websocket-test](https://github.com/jewelsea/javafx-websocket-test)做了一个JavaFX应用程序的概念验证。也许有些概念可以帮助你,但我记得看似简单的Java web socket API确实包含了一些陷阱。 – jewelsea