从服务器套接字获取实时数据并在javafx中显示
您好,我需要从我创建的服务器套接字中获取一些数据,并将其显示在javafx应用程序中 fx应用程序ie显示器每250ms刷新一次数据,服务器每2秒发送一次数据 我的代码/计划主要有三部分从服务器套接字获取实时数据并在javafx中显示,java,sockets,javafx,serversocket,Java,Sockets,Javafx,Serversocket,您好,我需要从我创建的服务器套接字中获取一些数据,并将其显示在javafx应用程序中 fx应用程序ie显示器每250ms刷新一次数据,服务器每2秒发送一次数据 我的代码/计划主要有三部分 1.服务器生成数据并每隔2秒将其发送到端口 2.Clint代码从服务器获取数据并更新其全局变量 3.每250ms计划执行者接触clint中的全局变量并更新文本字段 //遗憾的是,这似乎不起作用 我总是先启动服务器,然后clint再运行应用程序 所以我写的代码如下 服务器代码 import java.io.Dat
1.服务器生成数据并每隔2秒将其发送到端口
2.Clint代码从服务器获取数据并更新其全局变量
3.每250ms计划执行者接触clint中的全局变量并更新文本字段
//遗憾的是,这似乎不起作用
我总是先启动服务器,然后clint再运行应用程序 所以我写的代码如下 服务器代码
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket;
try (ServerSocket serverSocket = new ServerSocket(5555)) {
System.out.println("A");
socket = serverSocket.accept();
System.out.println("B");
if(socket.isConnected())System.out.println("Connected");
DataOutputStream dout=new DataOutputStream(socket.getOutputStream());
while (socket.isConnected()) {
String T=DataStructureMaker();
dout.writeUTF(T);
System.out.println(T);
TimeUnit.SECONDS.sleep(2);
dout.flush();
}
}
socket.close();
}
public static String DataStructureMaker()
{
float RV_Phase=0,RI_Phase=0,RI_Grid=0,RV_Grid=0;String s="";
DecimalFormat df = new DecimalFormat("#.00");
s="";
RV_Phase=Float.parseFloat(df.format((Math.random()*10)));
s=s+Float.toString(RV_Phase)+"#";
RI_Phase=Float.parseFloat(df.format((Math.random()*10)));
s=s+Float.toString(RI_Phase)+"#";
RI_Grid=Float.parseFloat(df.format((Math.random()*10)));
s=s+Float.toString(RI_Grid)+"#";
RV_Grid=Float.parseFloat(df.format((Math.random()*10)));
s=s+Float.toString(RV_Grid)+"#";
return s;
}
}
clint代码是
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
public class Clint {
public static String RV_Grid;
public static String RI_Grid;
public static String RI_Phase;
public static String RV_Phase;
public static void main(String[] args) throws IOException, InterruptedException {
Socket s=new Socket("localhost",5555);
String S;
DataInputStream dIn=new DataInputStream(s.getInputStream());
while (s.isConnected()) {
S=dIn.readUTF();
setData(S);
}
}
public static void setData(String S) // Decryt data and set global values
{
char[] A=S.toCharArray();
int HC=0;
String R="";
if(A.length>2)
for(char x:A)
{
if(x=='#')
{
switch(HC)
{
case 0:
HC++;
RV_Phase=R;
R="";
break;
case 1:
HC++;
RI_Phase=R;
R="";
break;
case 2:
HC++;
RI_Grid=R;
R="";
break;
case 3:
HC++;
RV_Grid=R;
R="";
break;
}
}else{
R=R+x;
}
}
}
}
最后是我的fxml控制器
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
public class FXMLDocumentController implements Initializable {
@FXML
private TextField Text1;
@FXML
private TextField Text2;
@FXML
private TextField Text3;
@FXML
private TextField Text4;
static ScheduledExecutorService scheduledExecutorService;
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(() -> {
setData();
}, 0, 250, TimeUnit.MILLISECONDS);
}
public void setData()
{
Text1.setText(Clint.RI_Grid);
Text2.setText(Clint.RI_Phase);
Text3.setText(Clint.RV_Grid);
Text4.setText(Clint.RV_Phase);
}
}
上面的代码包含了我的程序加密、解密、发送、接收和显示所需的所有重要数据。下面的代码有两个目的:
一个是演示对所问问题的回答。
另一个是使用客户端接收的数据动态更新GUI的解决方案
代码的目的不是展示如何正确实现客户机和服务器 关于MRE,有几点需要注意:
1.它不应代表您的特定应用程序,而应代表您关注的特定问题 我们正在努力解决这个问题
2.应该是M:去掉所有不必要的东西。最低限度
3.应该是R:重现问题
4.它应该是完整的。独立于数据库、文件或其他不可用的资源。
5.它应该易于使用(复制过去)
为了使用客户端接收到的数据动态更新GUI,我使用了一个共享模型,该模型由客户端更新。
通过使用绑定在GUI中反映的模型ifs中的任何更改
为了简单易用,我将控制器、模型、服务器和客户端都放在一个文件中
FXMLDocumentController.java
:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class FXMLDocumentController implements Initializable {
private static int PORT_NUMBER = 5555;
@FXML
private TextField text1;
@FXML
Button stopButton, startButton;
private ScheduledExecutorService scheduledExecutorService;
private Model model;
private Client client;
private Server server;
@Override
public void initialize(URL url, ResourceBundle rb) {
startButton.setDisable(false);
stopButton.setDisable(true);
scheduledExecutorService = Executors.newScheduledThreadPool(2);
model = new Model();
text1.textProperty().bind(model.getTextProperty());
}
private void startServer(){
try {
server = new Server(PORT_NUMBER);
server.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void startClient(){
try {
client = new Client(PORT_NUMBER, model);
client.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void start(){
scheduledExecutorService.submit(() -> startServer());
scheduledExecutorService.submit(() -> startClient());
startButton.setDisable(true);
stopButton.setDisable(false);
}
public void stop(){
client.stop();
server.stop();
scheduledExecutorService.shutdown();
stopButton.setDisable(true);
}
}
class Model {
private final ReadOnlyStringWrapper textProperty;
Model() {
textProperty = new ReadOnlyStringWrapper();
}
synchronized void setText(String s){
Platform.runLater(()->textProperty.set(s));
}
ReadOnlyStringWrapper getTextProperty(){
return textProperty;
}
}
class Server {
private final int portNumber;
private volatile boolean stop = false;
private static long REFRESH_TIME = 2;
Server(int portNumber) {
this.portNumber = portNumber;
}
void start() throws IOException {
Socket socket;
try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
socket = serverSocket.accept();
DataOutputStream dout=new DataOutputStream(socket.getOutputStream());
while (socket.isConnected() && ! stop) {
dout.writeUTF(randomText());
try {
TimeUnit.SECONDS.sleep(REFRESH_TIME);
} catch (InterruptedException ex) {
break;
}
dout.flush();
}
}
}
private String randomText()
{
DecimalFormat df = new DecimalFormat("#.00");
StringBuilder sb = new StringBuilder(df.format(Math.random()*10));
sb.append("#")
.append(df.format(Math.random()*10)) ;
return sb.toString();
}
void stop(){
stop = true;
}
}
class Client {
private final int portNumber;
private final Model model;
private volatile boolean stop = false;
Client(int portNumber, Model model) {
this.portNumber = portNumber;
this.model = model;
}
void start() throws IOException {
Socket socket = new Socket("localhost",portNumber);
DataInputStream dIn=new DataInputStream(socket.getInputStream());
while (socket.isConnected() && ! stop) {
model.setText(dIn.readUTF());
}
socket.close();
}
void stop(){
stop = true;
}
}
该控制器由FXMLDocument.fxml使用:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" prefHeight="113.0" prefWidth="232.0" spacing="10" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx_tests.FXMLDocumentController">
<TextField fx:id="text1" alignment="CENTER" promptText="Press START " />
<GridPane>
<Button fx:id="startButton" maxWidth="Infinity" onAction="#start" text="START" GridPane.columnIndex="0" />
<Button fx:id="stopButton" maxWidth="Infinity" onAction="#stop" text="STOP" GridPane.columnIndex="2" />
</GridPane>
</VBox>
您无法从
服务器sockst
获取任何数据,而且TCP、JavaFX或Java没有任何实时性。@user207421我不明白服务器何时发送数据,客户即时获取数据,这对我来说已经足够好了。请发帖子,这样我们就不必猜测丢失的信息。到底是什么问题?请提供一个演示出错原因的示例(请注意M),并遵守java命名约定。此代码从计划执行器服务创建的后台线程更新TextField
。您需要包装更新模型的调用或更新Platform.runLater(…)
中文本字段的调用(后者需要使用侦听器而不是绑定)。在客户端
和服务器
类中,您需要使stop()
方法同步
,或者将stop
声明为volatile
;目前无法保证运行循环的线程将“看到”另一个线程对停止
所做的任何更改。@kleopatra对,谢谢RandomText()typo@James_D谢谢你的评论。后台线程通过textProperty.set更新SimpleStringProperty
代码>。您的意思是必须从JavaFx线程更新该属性吗?是的,因为TextField
的textProperty()
已绑定到它,因此在后台线程上更新该属性会导致在同一后台线程上更新文本字段的文本。您应该在Platform.runLater()
中包装对textProperty().set的调用,或者您应该在model.textProperty()
上用一个侦听器替换绑定,该侦听器在Platform.runLater()中包装对text1.setText(…)
的调用,谢谢。我在模型synchronized void setText(String s){Platform.runLater(()->textProperty.set(s));}
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class FxmlTest extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
Pane root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}