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