我必须使用套接字FX创建一封带有JavaFX的电子邮件,FXML。我需要创建一个服务器和 3 个客户端,它们是我的三个帐户,它们必须并行启动。每个客户端都必须有一个关联的线程,但我的问题是:当我启动第一个客户端时,它可以工作,因此 FXML 文件打开。但是当我尝试打开第二个客户端时,Intellij 显示一个弹出窗口,对我说:停止并重新运行。在我的FXML中,我有一个连接按钮,我必须在其中选择一个帐户,然后我的服务器显示"连接"。如何解决此问题?开设多个客户端?如果你不明白,我会尽量更具体。
MailClient.java
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MailClient extends Application {
@Override
public void start(Stage stage) {
try{
Parent root = FXMLLoader.load(getClass().getResource("FXMLMailClient.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1", 4445);
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
launch(args);
}
}
Server.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
private int port = 4445;
private ServerSocket s = null;
private static ArrayList<ServerThread> clients = new ArrayList<>();
private static ExecutorService pool = Executors.newFixedThreadPool(3);
public void activate() throws IOException {
try {
s = new ServerSocket(port);
while (true) {
Socket s1 = s.accept();
System.out.println("Server connect");
ServerThread st1 = new ServerThread(s1);
clients.add(st1);
pool.execute(st1);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}finally{
s.close();
}
}
public static void main(String[] args) throws IOException {
Server s = new Server();
s.activate();
}
}
ServerThread.java
import java.io.IOException;
import java.net.Socket;
class ServerThread implements Runnable {
private Socket socket = null;
public ServerThread(Socket socket) throws IOException {
this.socket = socket;
}
@Override
public void run() {
System.out.println("Connected");
// during the run, the following cases will be handled:
// write an email, receive an email, delete an email.
}
}
FXMLMailClientController.java
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
public class FXMLMailClientController {
private boolean isConnected = false;
@FXML
private void handleConnectAction(ActionEvent event) throws IOException {
while (!isConnected) {
System.out.println("Client connect");
isConnected = true;
}
}
}
FXMLMailClient.fxml
<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="583.0" prefWidth="994.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.FXMLMailClientController">
<children>
<Button fx:id="connectClient" layoutX="70.0" layoutY="116.0" mnemonicParsing="false" onAction="#handleConnectAction" prefWidth="85.0" text="Connetti" />
<Label fx:id="account" layoutX="383.0" layoutY="14.0" prefWidth="344.0" text="" />
<ChoiceBox fx:id="choiceAccount" layoutY="83.0" prefHeight="25.0" prefWidth="225.0">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="email1" />
<String fx:value="email2" />
<String fx:value="email3" />
</FXCollections>
</items>
</ChoiceBox>
</children>
</AnchorPane>
以下 MRE 演示支持多个客户端的服务器。
JavaFxApplication
通常是入口点。它应该用于开始此示例:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MailClient extends Application {
//todo add support to start / stop the server and al clents
private final static int PORT_NUMBER = 4445;
@Override
public void start(Stage stage) {
try{
Parent root = FXMLLoader.load(getClass().getResource("FXMLMailClient.fxml"));
//todo path port number to FXMLMailClientController
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
new Server(PORT_NUMBER).activate(); //todo start server ftom gui
launch(args);
}
}
Server
持续接受连接。连接的每个客户端都由单独的线程(ServerThread
(处理。Server
只是打印出从客户端收到的任何消息:
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
private final ExecutorService pool;
private final List<ServerThread> clients;
private final int portNumber;
private boolean stop;
Server(int portNumber) {
this.portNumber = portNumber;
pool = Executors.newFixedThreadPool(3);
clients = new ArrayList<>();
}
private void runServer(){
System.out.println("SERVER: Waiting for client");
try{
ServerSocket serverSocket = new ServerSocket(portNumber);
stop = false;
while(! stop){//do in loop to support multiple clients
Socket clientSocket = serverSocket.accept();
System.out.println("SERVER: client connected");
ServerThread st1 = new ServerThread(clientSocket);
pool.execute(st1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop(){
for( ServerThread st : clients) {
st.stopServerTread();
}
stop = true;
pool.shutdown();
}
public void activate(){
new Thread(()->runServer()).start();
}
}
class ServerThread extends Thread {
private Socket socket = null;
private boolean stop;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try{
stop = false;
DataInputStream in = new DataInputStream( socket.getInputStream() );
String fromClient;
while(!stop){
if((fromClient = in.readUTF()) != null) {
System.out.println("SERVER: recieved message - " + fromClient);
}
}
} catch (IOException e) {
e.printStackTrace();;
}
}
void stopServerTread(){
stop = true;
}
}
FXMLMailClient.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane id="AnchorPane" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="tests.FXMLMailClientController">
<children>
<Button fx:id="connectClient" layoutX="70.0" layoutY="120.0" mnemonicParsing="false"
onAction="#handleConnectAction" prefWidth="85.0" text="Connetti" />
<ChoiceBox fx:id="choiceAccount" layoutY="85.0" prefHeight="25.0" prefWidth="225.0">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="email1" />
<String fx:value="email2" />
<String fx:value="email3" />
</FXCollections>
</items>
</ChoiceBox>
</children>
</AnchorPane>
控制器在按下按钮时构造一个新Client
:
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
public class FXMLMailClientController {
private final String hostName = "localhost";
private final int portNumber = 4445;
@FXML
ChoiceBox<String> choiceAccount;
@FXML
private void handleConnectAction(ActionEvent event) throws IOException {
if(choiceAccount.getSelectionModel().getSelectedItem() == null) return;
new Client(choiceAccount.getSelectionModel().getSelectedItem(), hostName, portNumber).activate();
}
}
Client
不断向Server
发送带有时间戳的消息:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
public class Client{
private final String account, hostName;
private final int portNumber;
private boolean stop;
Client(String account, String hostName, int portNumber ) {
this.account = account;
this.hostName = hostName;
this.portNumber = portNumber;
}
private void runClient(){
try {
stop = false;
Socket socket = new Socket(hostName, portNumber);
DataInputStream in = new DataInputStream( socket.getInputStream() );
DataOutputStream out = new DataOutputStream( socket.getOutputStream() );
out.writeUTF(getClientMessage());
while (! stop) {
TimeUnit.SECONDS.sleep(3);
out.writeUTF(getClientMessage());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void activate(){
new Thread(()->runClient()).start();
}
public void stop(){
stop = true;
}
private String getClientMessage(){
LocalTime time = LocalTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
return "from client # "+ account + " at "+ time.format(formatter);
}
}
请注意,在这个简单的演示中,客户端数量不限于 3。
也可以连接具有相同帐户的两个客户端。