简介
本节我们利用BIO来实现一个简易版的多人在线的聊天室,该程序有以下几个特点:
- 基于BIO模型
- 支持多人同时在线
- 每位用户的发言都会转发给其他在线的用户
BIO编程模型
关于BIO的知识可以自行百度,此处不细讲。
时序图演示
效果展示
首先我们启动四个进程,一个服务端进程先启动,然后依次启动三个客户端进程,此时服务端进程会打印客户端的连接信息
然后,我们依次用客户端1,2,3,发送消息,每个客户端发送的消息都将会被转发到其他客户端,服务端也打印所有消息。
然后演示,当一个客户端输入”quit”后,将离线并失去客户端连接并关闭Socket
服务端也打印出信息
代码演示
注意以下代码包含Tab标签
服务端代码
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; /** * @author geekerstar * @description 服务端 */ 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<>(); } /** * 添加客户端 * * @param socket */ 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 + "]已连接到服务器"); } } /** * 移除客户端 * * @param socket * @throws IOException */ 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 + "]已断开连接"); } } /** * 转发消息 * * @param socket * @param fwdMsg */ 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(); } } } /** * 监听是否准备退出标志 * * @param msg * @return */ public boolean readyToQuit(String msg) { return QUIT.equals(msg); } /** * 关闭serverSocket */ 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(); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author geekerstar * @description */ 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 = "客户端[" + socket.getPort() + "]:" + msg + "\n"; System.out.println(fwdMsg); // 将消息转发给聊天室里在线的其他用户 server.forwardMessage(socket, fwdMsg); // 检查用户是否准备退出 if (server.readyToQuit(msg)) { break; } } } catch (IOException e) { e.printStackTrace(); } finally { try { server.removeClient(socket); } catch (IOException e) { e.printStackTrace(); } } } }
客户端代码
import java.io.*; import java.net.Socket; /** * @author geekerstar * @description 客户端 */ 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; /** * 发送消息给服务器 * * @param msg * @throws IOException */ public void send(String msg) throws IOException { if (!socket.isOutputShutdown()) { writer.write(msg + "\n"); writer.flush(); } } /** * 从服务器接收消息 * * @return * @throws IOException */ public String receive() throws IOException { String msg = null; if (!socket.isInputShutdown()) { msg = reader.readLine(); } return msg; } /** * 检查用户是否准备退出 * * @param msg * @return */ public boolean readyToQuit(String msg) { return QUIT.equals(msg); } /** * 关闭socket */ public void close() { if (writer != null) { try { System.out.println("关闭socket"); writer.close(); } catch (IOException e) { e.printStackTrace(); } } } public void start() { 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 Thread(new UserInputHandler(this)).start(); // 读取服务器转发的消息 String msg = null; while ((msg = receive()) != null) { System.out.println(msg); } } catch (IOException e) { e.printStackTrace(); } finally { close(); } } public static void main(String[] args) { ChatClient chatClient = new ChatClient(); chatClient.start(); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author geekerstar * @description */ public class UserInputHandler implements Runnable { private ChatClient chatClient; public UserInputHandler(ChatClient chatClient) { this.chatClient = chatClient; } @Override public void run() { BufferedReader consoleReader = new BufferedReader( new InputStreamReader(System.in) ); while (true) { try { String input = consoleReader.readLine(); // 向服务器发送消息 chatClient.send(input); // 检查用户是否准备退出 if (chatClient.readyToQuit(input)) { break; } } catch (IOException e) { e.printStackTrace(); } } } }
常见问题FAQ
- 如果资源链接失效了怎么办?
- 本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
- 如果用户分享的资源与描述不符怎么办?
- 如何分享个人资源获取赞助积分或其他奖励?
- 如果您发现了本资源有侵权行为怎么办?