尝试使用JavaFX创建一个多客户端聊天服务器我是java编程的新手所以我看到一些旧的聊天应用程序通过使用从那时起的一些代码我创建这个直到将在下面显示的代码。
我将swing转换为javaFX因为swing&现在已经老了。所以在Swing中我们可以创建一个construtor,所以ServerGUI.java或ClientGUI.java可以通过构造函数默认创建和调用 但是在javaFX中,当我们从Application扩展它时,我们需要覆盖fuction primaryStage(),但是没有构造可以在其中生成。我试试看&编译它的返回异常错误。 我怎么能这样做是JAVAFX而不是构造函数
直到我无法将GUI版本连接到简单版本我做错了因为我再说它连接它需要构造函数但是在javaFx中没有构造函数 如果用户想通过GUI版本输入,我如何在javaFX中连接。
参考链接 https://docs.oracle.com/javafx/2/get_started/hello_world.htm https://blogs.oracle.com/jtc/entry/javafx_sockets_and_threading_lessons https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/swing-fx-interoperability.htm http://www.dreamincode.net/forums/topic/262304-simple-client-and-server-chat-program/ http://www.dreamincode.net/forums/topic/259777-a-simple-chat-program-with-clientserver-gui-optional/
ChatMessage
import java.io.*;
/*
* This class defines the different type of messages that will be exchanged between the
* Clients and the Server.
* When talking from a Java Client to a Java Server a lot easier to pass Java objects, no
* need to count bytes or to wait for a line feed at the end of the frame
*/
public class ChatMessage implements Serializable {
protected static final long serialVersionUID = 1112122200L;
// The different types of message sent by the Client
// WHOISIN to receive the list of the users connected
// MESSAGE an ordinary message
// LOGOUT to disconnect from the Server
static final int WHOISIN = 0, MESSAGE = 1, LOGOUT = 2;
private int type;
private String message;
// constructor
ChatMessage(int type, String message) {
this.type = type;
this.message = message;
}
// getters
int getType() {
return type;
}
String getMessage() {
return message;
}
}
Server.java
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
// The server that can be run both as a console application or a GUI
/*
* To run as a console application just open a console window and:
* > java Server
* > java Server portNumber
* If the port number is not specified 1500 is used
*/
public class Server {
// a unique ID for each connection
private static int uniqueId;
// an ArrayList to keep the list of the Client
private ArrayList<ClientThread> al;
// if I am in a GUI
private ServerGUI sg;
// to display time
private SimpleDateFormat sdf;
// the port number to listen for connection
private int port;
// the boolean that will be turned of to stop the server
private boolean keepGoing;
/*
* server constructor that receive the port to listen to for connection as parameter
* in console
*/
public Server(int port) {
this(port, null);
}
public Server(int port, ServerGUI sg) {
// GUI or not
this.sg = sg;
// the port
this.port = port;
// to display hh:mm:ss
sdf = new SimpleDateFormat("HH:mm:ss");
// ArrayList for the Client list
al = new ArrayList<ClientThread>();
}
public void start() {
keepGoing = true;
// create socket server and wait for connection requests
try
{
// the socket used by the server
ServerSocket serverSocket = new ServerSocket(port);
// infinite loop to wait for connections
while(keepGoing)
{
// format message saying we are waiting
display("Server waiting for Clients on port " + port + ".");
Socket socket = serverSocket.accept(); // accept connection
// if I was asked to stop
if(!keepGoing)
break;
ClientThread t = new ClientThread(socket); // make a thread of it
al.add(t); // save it in the ArrayList
t.start();
}
// I was asked to stop
try {
serverSocket.close();
for(int i = 0; i < al.size(); ++i) {
ClientThread tc = al.get(i);
try {
tc.sInput.close();
tc.sOutput.close();
tc.socket.close();
}
catch(IOException ioE) {
// not much I can do
}
}
}
catch(Exception e) {
display("Exception closing the server and clients: " + e);
}
}
// something went bad
catch (IOException e) {
String msg = sdf.format(new Date()) + " Exception on new ServerSocket: " + e + "\n";
display(msg);
}
}
// For the GUI to stop the server
protected void stop() {
keepGoing = false;
// connect to myself as Client to exit statement
// Socket socket = serverSocket.accept();
try {
new Socket("localhost", port);
}
catch(Exception e) {
// nothing I can really do
}
}
/*
* Display an event (not a message) to the console or the GUI
*/
private void display(String msg) {
String time = sdf.format(new Date()) + " " + msg;
if(sg == null)
System.out.println(time);
else
sg.appendEvent(time + "\n");
}
// to broadcast a message to all Clients
private synchronized void broadcast(String message) {
// add HH:mm:ss and \n to the message
String time = sdf.format(new Date());
String messageLf = time + " " + message + "\n";
// display message on console or GUI
if(sg == null)
System.out.print(messageLf);
else
sg.appendRoom(messageLf); // append in the room window
// we loop in reverse order in case we would have to remove a Client
// because it has disconnected
for(int i = al.size(); --i >= 0;) {
ClientThread ct = al.get(i);
// try to write to the Client if it fails remove it from the list
if(!ct.writeMsg(messageLf)) {
al.remove(i);
display("Disconnected Client " + ct.username + " removed from list.");
}
}
}
// for a client who logoff using the LOGOUT message
synchronized void remove(int id) {
// scan the array list until we found the Id
for(int i = 0; i < al.size(); ++i) {
ClientThread ct = al.get(i);
// found it
if(ct.id == id) {
al.remove(i);
return;
}
}
}
public static void main(String[] args) {
// start server on port 1500 unless a PortNumber is specified
int portNumber = 1500;
switch(args.length) {
case 1:
try {
portNumber = Integer.parseInt(args[0]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Server [portNumber]");
return;
}
case 0:
break;
default:
System.out.println("Usage is: > java Server [portNumber]");
return;
}
// create a server object and start it
Server server = new Server(portNumber);
server.start();
}
// One instance of this thread will run for each client
class ClientThread extends Thread {
// the socket where to listen/talk
Socket socket;
ObjectInputStream sInput;
ObjectOutputStream sOutput;
// unique id (easier for deconnection)
int id;
// the Username of the Client
String username;
// the only type of message a will receive
ChatMessage cm;
// the date I connect
String date;
// Constructore
ClientThread(Socket socket) {
// a unique id
id = ++uniqueId;
this.socket = socket;
/* Creating both Data Stream */
System.out.println("Thread trying to create Object Input/Output Streams");
try
{
// create output first
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new ObjectInputStream(socket.getInputStream());
// read the username
username = (String) sInput.readObject();
display(username + " just connected.");
}
catch (IOException e) {
display("Exception creating new Input/output Streams: " + e);
return;
}
// have to catch ClassNotFoundException
catch (ClassNotFoundException e) {
}
date = new Date().toString() + "\n";
}
// what will run forever
public void run() {
// to loop until LOGOUT
boolean keepGoing = true;
while(keepGoing) {
// read a String (which is an object)
try {
cm = (ChatMessage) sInput.readObject();
}
catch (IOException e) {
display(username + " Exception reading Streams: " + e);
break;
}
catch(ClassNotFoundException e2) {
break;
}
// the messaage part of the ChatMessage
String message = cm.getMessage();
// Switch on the type of message receive
switch(cm.getType()) {
case ChatMessage.MESSAGE:
broadcast(username + ": " + message);
break;
case ChatMessage.LOGOUT:
display(username + " disconnected with a LOGOUT message.");
keepGoing = false;
break;
case ChatMessage.WHOISIN:
writeMsg("List of the users connected at " + sdf.format(new Date()) + "\n");
// scan al the users connected
for(int i = 0; i < al.size(); ++i) {
ClientThread ct = al.get(i);
writeMsg((i+1) + ") " + ct.username + " since " + ct.date);
}
break;
}
}
// remove from the arrayList containing the list of the
// connected Clients
remove(id);
close();
}
// try to close everything
private void close() {
// try to close the connection
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {}
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {};
try {
if(socket != null) socket.close();
}
catch (Exception e) {}
}
// Write a String to the Client output stream
private boolean writeMsg(String msg) {
// if Client is still connected send the message to it
if(!socket.isConnected()) {
close();
return false;
}
// write the message to the stream
try {
sOutput.writeObject(msg);
}
// if an error occurs, do not abort just inform the user
catch(IOException e) {
display("Error sending message to " + username);
display(e.toString());
}
return true;
}
}
}
ServerGUI.java
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class ServerGUI extends Application {
final HBox hb = new HBox();
final VBox vbox = new VBox();
final VBox vbox2 = new VBox();
private Button stopStart;
// TextArea for the chat room and the events
private TextArea chat, event;
// The port number
private TextField tPortNumber;
// my server
private Server server;
final ObservableList<Client> data = FXCollections.observableArrayList();
final ObservableList<String> names = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
//new ServerGUI(1500);
}
/*public ServerGUI(int port) {
super();
server = null;
this.port = port;
public ServerGUI(int port) throws InvocationTargetException,
{
//super("Chat Server");
server = null;
this.port = port;
}*/
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(new Group());
primaryStage.setTitle("Server GUI");
primaryStage.setWidth(530);
primaryStage.setHeight(490);
final TextField portNo = new TextField();
Label portLabel = new Label("Port Number");
portLabel.setFont(new Font("Arial", 12));
portNo.setPromptText("Port No ");
portNo.setEditable(true);
final Button startandStop = new Button("Start");
startandStop.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
startStopButton() ;
}
});
final TextArea chatArea = new TextArea();
chatArea.setPromptText("Chat Room");
chatArea.setEditable(false);
final TextArea eventLog = new TextArea();
eventLog.setPromptText("Event Log");
eventLog.setEditable(false);
eventLog.setScrollTop(100);
hb.setSpacing(3);
hb.setPadding(new Insets(10, 0, 0, 10));
hb.getChildren().addAll(portLabel, portNo, startandStop);
hb.setCenterShape(true);
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(chatArea, eventLog);
vbox2.setSpacing(5);
vbox2.setPadding(new Insets(10, 0, 0, 10));
vbox2.getChildren().addAll(hb,vbox);
((Group) scene.getRoot()).getChildren().addAll(vbox2);
primaryStage.setScene(scene);
primaryStage.show();
}
// start or stop where clicked
public void startStopButton(){
// if running we have to stop
if(server != null) {
server.stop();
server = null;
stopStart.setText("Start");
return;
}
// OK start the server
int port;
try {
port = Integer.parseInt(tPortNumber.getText().trim());
}
catch(Exception er) {
appendEvent("Invalid port number");
return;
}
// ceate a new Server
server = new Server(port, this);
// and start it as a thread
new ServerRunning().start();
stopStart.setText("Stop");
tPortNumber.setEditable(false);
}
void appendRoom(String str) {
chat.appendText(str);
chat.positionCaret(chat.getText().length() - 1);
}
void appendEvent(String str) {
event.appendText(str);
event.positionCaret(chat.getText().length() - 1);
}
// A thread to run the Server
class ServerRunning extends Thread {
public void run() {
server.start(); // should execute until if fails
// the server failed
stopStart.setText("Start");
tPortNumber.setEditable(true);
appendEvent("Server crashed\n");
server = null;
}
}
}
Client.java
import java.net.*;
import java.io.*;
import java.util.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
/*
* The Client that can be run both as a console or a GUI
*/
/*
* To start the Client in console mode use one of the following command
* > java Client
* > java Client username
* > java Client username portNumber
* > java Client username portNumber serverAddress
* at the console prompt
* If the portNumber is not specified 1500 is used
* If the serverAddress is not specified "localHost" is used
* If the username is not specified "Anonymous" is used
* > java Client
* is equivalent to
* > java Client Anonymous 1500 localhost
*/
public class Client {
// for I/O
private ObjectInputStream sInput; // to read from the socket
private ObjectOutputStream sOutput; // to write on the socket
private Socket socket;
// if I use a GUI or not
private ClientGUI cg;
// the server, the port and the username
private String server, username;
private int port;
/*
* Constructor called by console mode
* server:: the server address
* port:: the port number
* username:: the username
*/
Client(String server, int port, String username) {
this(server, port, username, null);
}
/*
* Constructor call when used from a GUI
* in console mode the ClienGUI parameter is null
*/
Client(String server, int port, String username, ClientGUI cg) {
this.server = server;
this.port = port;
this.username = username;
// save if we are in GUI mode or not
this.cg = cg;
}
public boolean start() {
// try to connect to the server
try {
socket = new Socket(server, port);
}
// if it failed not much I can so
catch(Exception ec) {
display("Error connectiong to server:" + ec);
return false;
}
String msg = "Connection accepted " + socket.getInetAddress() + ":" + socket.getPort();
display(msg);
// Creating both Data Stream
try
{
sInput = new ObjectInputStream(socket.getInputStream());
sOutput = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException eIO) {
display("Exception creating new Input/output Streams: " + eIO);
return false;
}
// creates the Thread to listen from the server
new ListenFromServer().start();
// Send our username to the server this is the only message that we
// will send as a String. All other messages will be ChatMessage objects
try
{
sOutput.writeObject(username);
}
catch (IOException eIO) {
display("Exception doing login : " + eIO);
disconnect();
return false;
}
// success we inform the caller that it worked
return true;
}
// To send a message to the console or the GUI
private void display(String msg) {
if(cg == null)
System.out.println(msg); // println in console mode
else
cg.append(msg + "\n"); // append to the ClientGUI JTextArea (or whatever)
}
// To send a message to the server
void sendMessage(ChatMessage msg) {
try {
sOutput.writeObject(msg);
}
catch(IOException e) {
display("Exception writing to server: " + e);
}
}
// Close the Input/Output streams and disconnect not much to do in the catch clause
private void disconnect() {
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {}
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {}
try{
if(socket != null) socket.close();
}
catch(Exception e) {}
// inform the GUI
if(cg != null)
cg.connectionFailed();
}
public static void main(String[] args) {
// default values
int portNumber = 1500;
String serverAddress = "localhost";
String userName = "Anonymous";
// depending of the number of arguments provided we fall through
switch(args.length) {
// > javac Client username portNumber serverAddr
case 3:
serverAddress = args[2];
// > javac Client username portNumber
case 2:
try {
portNumber = Integer.parseInt(args[1]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Client [username] [portNumber] [serverAddress]");
return;
}
// > javac Client username
case 1:
userName = args[0];
// > java Client
case 0:
break;
// invalid number of arguments
default:
System.out.println("Usage is: > java Client [username] [portNumber] {serverAddress]");
return;
}
// create the Client object
Client client = new Client(serverAddress, portNumber, userName);
// test if we can start the connection to the Server
if(!client.start())
return;
// wait for messages from user
Scanner scan = new Scanner(System.in);
// loop forever for message from the user
while(true) {
System.out.print("> ");
// read message from user
String msg = scan.nextLine();
// logout if message is LOGOUT
if(msg.equalsIgnoreCase("LOGOUT")) {
client.sendMessage(new ChatMessage(ChatMessage.LOGOUT, ""));
// break to do the disconnect
break;
}
// message WhoIsIn
else if(msg.equalsIgnoreCase("WHOISIN")) {
client.sendMessage(new ChatMessage(ChatMessage.WHOISIN, ""));
}
else { // default to ordinary message
client.sendMessage(new ChatMessage(ChatMessage.MESSAGE, msg));
}
}
// done disconnect
client.disconnect();
}
/*
* a class that waits for the message from the server and append them to the TextArea
* if we have a GUI or simply System.out.println() it in console mode
*/
class ListenFromServer extends Thread {
public void run() {
while(true) {
try {
String msg = (String) sInput.readObject();
// if console mode print the message and add back the prompt
if(cg == null) {
System.out.println(msg);
System.out.print("> ");
}
else {
cg.append(msg);
}
}
catch(IOException e) {
display("Server has close the connection: " + e);
if(cg != null)
cg.connectionFailed();
break;
}
catch(ClassNotFoundException e2) {
}
}
}
}
}
ClientGUI.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class ClientGUI extends Application
{
private Label label;
// to hold the Username and later on the messages
private TextField tf;
// to hold the server address an the port number
private TextField tfServer, tfPort;
// to Logout and get the list of the users
private Button login, logout, whoIsIn;
// for the chat room
private TextArea ta;
// if it is for connection
private boolean connected;
// the Client object
private Client client;
// the default port number
private int defaultPort = 1500;
private String defaultHost = "localhost";
final HBox hb = new HBox();
final HBox hb2 = new HBox();
final HBox hb3 = new HBox();
final VBox vbox = new VBox();
final VBox vbox2 = new VBox();
public static void main(String[] args) {
launch(args);
//new ClientGUI("localhost", 1500);
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(new Group());
primaryStage.setTitle("Client GUI");
primaryStage.setWidth(740);
primaryStage.setHeight(400);
Label hostLabel = new Label("Server Address");
hostLabel.setFont(new Font("Arial", 12));
TextField localHost = new TextField();
localHost.setPromptText("Server Address");
localHost.setEditable(true);
tfServer = localHost;
final TextField portNo = new TextField();
Label portLabel = new Label("Port Number");
portLabel.setFont(new Font("Arial", 12));
portNo.setPromptText("Port No ");
portNo.setEditable(true);
tfPort = portNo;
final TextField userName = new TextField();
Label userLabel = new Label("Nick Name");
userLabel.setFont(new Font("Arial", 12));
userName.setPromptText("Nick Name");
userName.setEditable(true);
final Button loginButton = new Button("Login");
this.login = loginButton;
loginButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
String username = tf.getText().trim();
// empty username ignore it
if(username.length() == 0)
return;
// empty serverAddress ignore it
String server = tfServer.getText().trim();
if(server.length() == 0)
return;
// empty or invalid port numer, ignore it
String portNumber = tfPort.getText().trim();
if(portNumber.length() == 0)
return;
int port = 0;
try {
port = Integer.parseInt(portNumber);
}
catch(Exception en) {
return; // nothing I can do if port number is not valid
}
// try creating a new Client with GUI
//client = new Client(server, port, username, this);
// test if we can start the Client
if(!client.start())
return;
tf.setText("");
label.setText("Enter your message below");
connected = true;
}
});
final Button logoutButton = new Button("logout");
this.logout = logoutButton;
logoutButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
client.sendMessage(new ChatMessage(ChatMessage.LOGOUT, ""));
return;
}
});
final Button whoIsIn = new Button("whoIsIn");
this.whoIsIn = whoIsIn;
whoIsIn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
client.sendMessage(new ChatMessage(ChatMessage.WHOISIN, ""));
return;
}
});
final Button sendButton = new Button("Send");
sendButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// just have to send the message
client.sendMessage(new ChatMessage(ChatMessage.MESSAGE, tf.getText()));
tf.setText("");
return;
}
});
final TextField chatMessage = new TextField();
chatMessage.setPromptText("Enter Your Chat Message Here");
final TextArea chatRoom = new TextArea();
chatRoom.setPromptText("Welcome to the Chat room");
chatRoom.setEditable(false);
//chatRoom.setScrollTop(100);
hb.setSpacing(3);
hb.setPadding(new Insets(10, 0, 0, 10));
hb.getChildren().addAll(hostLabel, localHost, portLabel, portNo, userLabel, userName);
//hb.setCenterShape(true);
hb2.setSpacing(5);
hb2.setPadding(new Insets(10, 0, 0, 10));
hb2.getChildren().addAll(loginButton, logoutButton, whoIsIn);
hb3.setSpacing(5);
hb3.setPadding(new Insets(10, 0, 0, 10));
hb3.getChildren().addAll(chatMessage, sendButton);
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(hb3, chatRoom);
vbox2.setSpacing(5);
vbox2.setPadding(new Insets(10, 0, 0, 10));
vbox2.getChildren().addAll(hb, vbox, hb2);
((Group) scene.getRoot()).getChildren().addAll(vbox2);
primaryStage.setScene(scene);
primaryStage.show();
}
// called by the Client to append text in the TextArea
void append(String str) {
ta.appendText(str);
ta.positionCaret(ta.getText().length() - 1);
}
// called by the GUI is the connection failed
// we reset our buttons, label, textfield
void connectionFailed() {
login.setDisable(false);
logout.setDisable(true);
whoIsIn.setDisable(true);
label.setText("Enter your username below");
tf.setText("Anonymous");
// reset port number and host name as a construction time
tfPort.setText("" + defaultPort);
tfServer.setText(defaultHost);
// let the user change them
tfServer.setEditable(false);
tfPort.setEditable(false);
// don't react to a <CR> after the username
// tf.removeActionListener(this);
connected = false;
}
}