2021-06-14

本文介绍了如何使用JavaFX构建一个简单的多人聊天室,重点展示了如何运用InetAddress、ServerSocket和Socket类进行服务器与客户端的通信,以及如何利用BIO模式处理消息收发。通过实例代码,学习者能理解基础网络编程在GUI应用中的实践。
摘要由CSDN通过智能技术生成

常州大学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方法放到点击事件中。(本程序不知问什么在接收信息的时候会多接收一个换行和------------------>)(请求大佬援助)

实现效果

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值