01基于BIO的多人聊天室


聊天室服务端实现

聊天主干逻辑

 1 package server;
 2 
 3 import java.io.BufferedWriter;
 4 import java.io.IOException;
 5 import java.io.OutputStreamWriter;
 6 import java.io.Writer;
 7 import java.net.ServerSocket;
 8 import java.net.Socket;
 9 import java.util.HashMap;
10 import java.util.Map;
11 
12 public class ChatServer {
13 
14     private int DEFAULT_PORT = 8888;
15     private final String QUIT = "quit";
16 
17     private ServerSocket serverSocket;
18     private Map connectedClients;
19 
20     public ChatServer() {
21         connectedClients = new HashMap<>();
22     }
23 
24     public synchronized void addClient(Socket socket) throws IOException {
25         if (socket != null) {
26             int port = socket.getPort();
27             BufferedWriter writer = new BufferedWriter(
28                     new OutputStreamWriter(socket.getOutputStream())
29             );
30             connectedClients.put(port, writer);
31             System.out.println("客户端[" + port + "]已连接到服务器");
32         }
33     }
34 
35     public synchronized void removeClient(Socket socket) throws IOException {
36         if (socket != null) {
37             int port = socket.getPort();
38             if (connectedClients.containsKey(port)) {
39                 connectedClients.get(port).close();
40             }
41             connectedClients.remove(port);
42             System.out.println("客户端[" + port + "]已断开连接");
43         }
44     }
45 
46     public synchronized void forwardMessage(Socket socket, String fwdMsg) throws IOException {
47         for (Integer id : connectedClients.keySet()) {
48             if (!id.equals(socket.getPort())) {
49                 Writer writer = connectedClients.get(id);
50                 writer.write(fwdMsg);
51                 writer.flush();
52             }
53         }
54     }
55 
56     public boolean readyToQuit(String msg) {
57         return QUIT.equals(msg);
58     }
59 
60     public synchronized void close() {
61         if (serverSocket != null) {
62             try {
63                 serverSocket.close();
64                 System.out.println("关闭serverSocket");
65             } catch (IOException e) {
66                 e.printStackTrace();
67             }
68         }
69     }
70 
71     public void start() {
72 
73         try {
74             // 绑定监听端口
75             serverSocket = new ServerSocket(DEFAULT_PORT);
76             System.out.println("启动服务器,监听端口:" + DEFAULT_PORT + "...");
77 
78             while (true) {
79                 // 等待客户端连接
80                 Socket socket = serverSocket.accept();
81                 // 创建ChatHandler线程
82                 new Thread(new ChatHandler(this, socket)).start();
83             }
84 
85         } catch (IOException e) {
86             e.printStackTrace();
87         } finally {
88             close();
89         }
90     }
91 
92     public static void main(String[] args) {
93         ChatServer server = new ChatServer();
94         server.start();
95     }
96 
97 }

聊天任务

 1 package server;
 2 
 3 import java.io.*;
 4 import java.net.Socket;
 5 
 6 public class ChatHandler implements Runnable {
 7 
 8     private ChatServer server;
 9     private Socket socket;
10 
11     public ChatHandler(ChatServer server, Socket socket) {
12         this.server = server;
13         this.socket = socket;
14     }
15 
16     @Override
17     public void run() {
18         try {
19             // 存储新上线用户
20             server.addClient(socket);
21 
22             // 读取用户发送的消息
23             BufferedReader reader = new BufferedReader(
24                     new InputStreamReader(socket.getInputStream())
25             );
26 
27             String msg = null;
28             while ((msg = reader.readLine()) != null) {
29                 String fwdMsg = "客户端[" + socket.getPort() + "]: " + msg + "\n";
30                 System.out.print(fwdMsg);
31 
32                 // 将消息转发给聊天室里在线的其他用户
33                 server.forwardMessage(socket, fwdMsg);
34 
35                 // 检查用户是否准备退出
36                 if (server.readyToQuit(msg)) {
37                     break;
38                 }
39             }
40         } catch (IOException e) {
41             e.printStackTrace();
42         } finally {
43             try {
44                 server.removeClient(socket);
45             } catch (IOException e) {
46                 e.printStackTrace();
47             }
48         }
49     }
50 }

聊天室客户端

监听服务端推送的消息

 1 package client;
 2 
 3 import java.io.*;
 4 import java.net.Socket;
 5 
 6 public class ChatClient {
 7 
 8     private final String DEFAULT_SERVER_HOST = "127.0.0.1";
 9     private final int DEFAULT_SERVER_PORT = 8888;
10     private final String QUIT = "quit";
11 
12     private Socket socket;
13     private BufferedReader reader;
14     private BufferedWriter writer;
15 
16     // 发送消息给服务器
17     public void send(String msg) throws IOException {
18         if (!socket.isOutputShutdown()) {
19             writer.write(msg + "\n");
20             writer.flush();
21         }
22     }
23 
24     // 从服务器接收消息
25     public String receive() throws IOException {
26         String msg = null;
27         if (!socket.isInputShutdown()) {
28             msg = reader.readLine();
29         }
30         return msg;
31     }
32 
33     // 检查用户是否准备退出
34     public boolean readyToQuit(String msg) {
35         return QUIT.equals(msg);
36     }
37 
38     public void close() {
39         if (writer != null) {
40             try {
41                 System.out.println("关闭socket");
42                 writer.close();
43             } catch (IOException e) {
44                 e.printStackTrace();
45             }
46         }
47     }
48 
49     public void start() {
50 
51         try {
52             // 创建socket
53             socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
54 
55             // 创建IO流
56             reader = new BufferedReader(
57                     new InputStreamReader(socket.getInputStream())
58             );
59             writer = new BufferedWriter(
60                     new OutputStreamWriter(socket.getOutputStream())
61             );
62 
63             // 处理用户的输入
64             new Thread(new UserInputHandler(this)).start();
65 
66             // 读取服务器转发的消息
67             String msg = null;
68             while ((msg = receive()) != null) {
69                 System.out.println(msg);
70             }
71         } catch (IOException e) {
72             e.printStackTrace();
73         } finally {
74             close();
75         }
76     }
77 
78     public static void main(String[] args) {
79         ChatClient chatClient = new ChatClient();
80         chatClient.start();
81     }
82 }

处理用户输入

 1 package client;
 2 
 3 import client.ChatClient;
 4 import java.io.*;
 5 
 6 public class UserInputHandler implements Runnable {
 7 
 8     private ChatClient chatClient;
 9 
10     public UserInputHandler(ChatClient chatClient) {
11         this.chatClient = chatClient;
12     }
13 
14     @Override
15     public void run() {
16         try {
17             // 等待用户输入消息
18             BufferedReader consoleReader =
19                     new BufferedReader(new InputStreamReader(System.in));
20             while (true) {
21                 String input = consoleReader.readLine();
22 
23                 // 向服务器发送消息
24                 chatClient.send(input);
25 
26                 // 检查用户是否准备退出
27                 if (chatClient.readyToQuit(input)) {
28                     break;
29                 }
30             }
31         } catch (IOException e) {
32             e.printStackTrace();
33         }
34     }
35 }