常州大学Java实验五,JavaFx实现多人聊天室
仅供参考
问题描述:
(1)学习InetAddress类、ServerSocket类和Socket类的使用。
使用ServerSocket类和Socket类以及InetAddress类编写一个GUI实现一个简单的聊天程序,界面如下图所示。
代码实现
客户端的
package client;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class User extends Application {
private TextArea display;
private TextField name_txt;
private TextField mesage_txt;
public class ChatClient {
private final String DEFAULT_SERVER_HOST = "127.0.0.1";
private final int DEFAULT_SERVER_PORT = 8888;
private final String QUIT = "quit";
private Socket socket;
private BufferedReader reader;
private BufferedWriter writer;
// 发送消息给服务器
public synchronized void send(String msg) throws IOException {
if (!socket.isOutputShutdown()) {
writer.write(msg + "\n");
writer.flush();
}
}
// 检查用户是否准备退出
public synchronized boolean readyToQuit(String msg) {
return QUIT.equals(msg);
}
public synchronized void close() {
if (writer != null) {
try {
System.out.println("关闭socket");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void go() {
try {
// 创建socket
socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
// 创建IO流
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
// 读取服务器转发的消息
new MessageThread(reader,display,this).start();
} catch (IOException e) {
e.printStackTrace();
}finally {
System.out.println("结束了");
}
}
}
// 从服务器接收消息
public class MessageThread extends Thread{
private BufferedReader reader;
private TextArea textArea;
private ChatClient chatClient;
// 接收消息线程的构造方法
public MessageThread(BufferedReader reader, TextArea textArea,ChatClient chatClient) {
this.reader = reader;
this.textArea = textArea;
}
public void run() {
try {
// 读取用户发送的消息
String msg = null;
while (true) {
msg = reader.readLine();
System.out.println("已接收到");
String fwdMsg = msg + "\n"+"------------------------------->"+"\n";
textArea.appendText(fwdMsg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//界面
@Override
public void start(Stage stage) {
ChatClient chatClient =new ChatClient();
//大致框架
BorderPane root = new BorderPane();
//内置布局设置
//上半部分
display=new TextArea();
display.setFont(Font.font("Times New Roman",FontWeight.BOLD, FontPosture.ITALIC, 25));
display.setPrefSize(800, 500);
root.setTop(display);
//下半部分
HBox box=new HBox();
name_txt=new TextField();
name_txt.setPromptText("昵称");
name_txt.setFont(Font.font("Times New Roman",FontWeight.BOLD, FontPosture.ITALIC, 25));
name_txt.setPrefSize(100, 50);
mesage_txt=new TextField();
mesage_txt.setPromptText("请输入要发送的语句,不要换行");
mesage_txt.setFont(Font.font("Times New Roman",FontWeight.BOLD, FontPosture.ITALIC, 25));
mesage_txt.setPrefSize(500, 50);
Button send_button=new Button();
send_button.setFont(Font.font("Times New Roman",FontWeight.BOLD, FontPosture.ITALIC, 25));
send_button.setText("发送");
box.getChildren().addAll(name_txt,mesage_txt,send_button);
box.setSpacing(30);
box.setPadding(new Insets(25,20,25,20));
root.setBottom(box);
//发送功能按钮
send_button.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
// TODO Auto-generated method stub
String send_msg=name_txt.getText().toString()+"说:"+mesage_txt.getText().toString()+"\n";
if(chatClient.readyToQuit(mesage_txt.getText().toString()))
{
chatClient.close();
try {
chatClient.socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
chatClient.send(send_msg);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
send_msg=name_txt.getText().toString()+"说:"+mesage_txt.getText().toString()+"\n"+"---------------------------->"+"\n";
display.appendText(send_msg);
}
});
//舞台搭建
Scene scene = new Scene(root,800,600);
stage.setTitle("M78星云光之国聊天室");
stage.setScene(scene);
stage.show();
chatClient.go();
}
public static void main(String[] args) {
launch(args);
}
}
服务器
package server;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class ChatServer {
private int DEFAULT_PORT = 8888;
private final String QUIT = "quit";
private ServerSocket serverSocket;
private Map<Integer, Writer> connectedClients;
public ChatServer() {
connectedClients = new HashMap<>();
}
public synchronized void addClient(Socket socket) throws IOException {
if (socket != null) {
int port = socket.getPort();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
connectedClients.put(port, writer);
System.out.println("客户端[" + port + "]已连接到服务器");
}
}
public synchronized void removeClient(Socket socket) throws IOException {
if (socket != null) {
int port = socket.getPort();
if (connectedClients.containsKey(port)) {
connectedClients.get(port).close();
}
connectedClients.remove(port);
System.out.println("客户端[" + port + "]已断开连接");
}
}
public synchronized void forwardMessage(Socket socket, String fwdMsg) throws IOException {
for (Integer id : connectedClients.keySet()) {
if (!id.equals(socket.getPort())) {
Writer writer = connectedClients.get(id);
writer.write(fwdMsg);
writer.flush();
System.out.println("已发送信息,true");
}
}
}
public boolean readyToQuit(String msg) {
return QUIT.equals(msg);
}
public synchronized void close() {
if (serverSocket != null) {
try {
serverSocket.close();
System.out.println("关闭serverSocket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void start() {
try {
// 绑定监听端口
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口:" + DEFAULT_PORT + "...");
while (true) {
// 等待客户端连接
Socket socket = serverSocket.accept();
// 创建ChatHandler线程
new Thread(new ChatHandler(this, socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close();
}
}
public static void main(String[] args) {
ChatServer server = new ChatServer();
server.start();
}
}
package server;
import java.io.*;
import java.net.Socket;
public class ChatHandler implements Runnable {
private ChatServer server;
private Socket socket;
public ChatHandler(ChatServer server, Socket socket) {
this.server = server;
this.socket = socket;
}
@Override
public void run() {
try {
// 存储新上线用户
server.addClient(socket);
// 读取用户发送的消息
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
String msg = null;
while ((msg = reader.readLine()) != null) {
String fwdMsg = msg +"\n";
System.out.print(fwdMsg);
// 将消息转发给聊天室里在线的其他用户
server.forwardMessage(socket, fwdMsg);
System.out.println("已发送信息");
// 检查用户是否准备退出
if (server.readyToQuit(msg)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
server.removeClient(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
设计思路
本次实验是基于BIO实现的,与老师提供的图片有点出路,因为我设置的字体比较大,还有两个Label放不进去所以选择了折中的方法,采用setPromptText,来实现类似的效果,实验主要采用的就是BIO,不知道的小伙伴可以去查查,没有能力写AIO和NIO,只能用最简单的BIO,使用BIO实现好后,给接收信息的函数开一个子线程即可,因为采用的是While死循环,当然这种方法并不好,会占用CPU资源,所以可以单独开一个子线程放后台让它不断去接收信息,不然他会占用你的主线程,直接将你的客户端程序卡死。其它的并不用改什么了,再设置个点击事件即可,要将send方法放到点击事件中。(本程序不知问什么在接收信息的时候会多接收一个换行和------------------>)(请求大佬援助)