From 83b1f2661d6964f7946cd198a8a22d72c5da1dc0 Mon Sep 17 00:00:00 2001 From: yang <3214671554@qq.com> Date: Fri, 5 Dec 2025 11:23:42 +0800 Subject: [PATCH] echo-server --- README.md | 21 ++++-- .../java/com/example/echo/EchoServer.java | 69 +++++++++++++------ 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 4b3544a..e8a81a6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Echo Server -一个简单的TCP回声服务器,实现客户端与服务器之间的消息回传功能。 +一个支持多客户端的TCP回声服务器,实现客户端与服务器之间的消息回传功能。 ## 项目简介 -本项目包含一个基础的TCP回声服务器和相应的客户端,服务器将客户端发送的每条消息原样返回给客户端,直到客户端发送"bye"断开连接。 +本项目包含一个支持多客户端同时连接的TCP回声服务器和相应的客户端,服务器将客户端发送的每条消息原样返回给客户端,直到客户端发送"bye"断开连接。服务器还支持特殊命令"TIME"来获取服务器当前时间。 ## 技术栈 @@ -15,10 +15,12 @@ ## 功能特性 -- 单客户端连接处理 +- 多客户端并发连接处理 - 消息回声功能 - 支持断开连接命令("bye") -- 基础错误处理 +- 支持获取服务器时间命令("TIME") +- 连接和断开日志记录 +- 使用线程池提高并发处理效率 ## 项目结构 @@ -42,7 +44,7 @@ mvn clean compile ### 2. 运行服务器 ```bash -mvn exec:java -Dexec.mainClass="com.example.echo.EchoServer" +mvn exec:java ""-Dexec.mainClass=com.example.echo.EchoServer" ``` 或直接运行: @@ -74,6 +76,13 @@ java -cp target/classes com.example.echo.EchoClient - 默认服务器端口: 8888 - 客户端默认连接地址: localhost:8888 +## 特殊命令 + +- `bye` - 断开与服务器的连接 +- `TIME` - 获取服务器当前时间 + ## 测试 -输入任意消息到客户端,服务器将回传相同的消息加上"Echo: "前缀。输入"bye"可断开连接。 \ No newline at end of file +输入任意消息到客户端,服务器将回传相同的消息加上"Echo: "前缀。输入"bye"可断开连接。 + +可以同时运行多个客户端实例来测试服务器的并发处理能力。 \ No newline at end of file diff --git a/src/main/java/com/example/echo/EchoServer.java b/src/main/java/com/example/echo/EchoServer.java index f55f684..180cf2b 100644 --- a/src/main/java/com/example/echo/EchoServer.java +++ b/src/main/java/com/example/echo/EchoServer.java @@ -2,13 +2,15 @@ package com.example.echo; import java.io.*; import java.net.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** - * 单客户端Echo服务器 - * 这个服务器一次只处理一个客户端连接 + * 多客户端Echo服务器 + * 这个服务器可以同时处理多个客户端连接 */ public class EchoServer { private static final int PORT = 8888; @@ -17,7 +19,10 @@ public class EchoServer { private ExecutorService executor; public EchoServer() { - executor = Executors.newSingleThreadExecutor(); + // 创建一个缓存线程池来处理多个客户端 + // 使用线程池而不是为每个连接创建新线程,这是一种更高效的并发处理方式 + // CachedThreadPool会根据需要创建新线程,但在可用时会重用之前构造的线程 + executor = Executors.newCachedThreadPool(); } /** @@ -28,43 +33,60 @@ public class EchoServer { running = true; System.out.println("Echo Server已启动,监听端口: " + PORT); - // 等待客户端连接 - System.out.println("等待客户端连接..."); - Socket clientSocket = serverSocket.accept(); - System.out.println("客户端已连接: " + clientSocket.getRemoteSocketAddress()); - - handleClient(clientSocket); + // 循环等待客户端连接 + while (running) { + try { + Socket clientSocket = serverSocket.accept(); + System.out.println("客户端已连接: " + clientSocket.getRemoteSocketAddress()); + + // 为每个客户端创建一个新的任务 + executor.submit(() -> handleClient(clientSocket)); + } catch (IOException e) { + if (running) { + System.err.println("接受客户端连接时发生错误: " + e.getMessage()); + } + } + } } /** * 处理客户端连接 */ private void handleClient(Socket clientSocket) { + System.out.println("开始与客户端 " + clientSocket.getRemoteSocketAddress() + " 通信"); + try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { String inputLine; - System.out.println("开始与客户端通信..."); while ((inputLine = in.readLine()) != null) { + System.out.println("从客户端 " + clientSocket.getRemoteSocketAddress() + " 收到消息: " + inputLine); + if ("bye".equalsIgnoreCase(inputLine.trim())) { - System.out.println("客户端请求断开连接"); + System.out.println("客户端 " + clientSocket.getRemoteSocketAddress() + " 请求断开连接"); + out.println("再见!"); break; } - System.out.println("收到消息: " + inputLine); - // 将收到的消息回显给客户端 - out.println("Echo: " + inputLine); + // 检查是否是TIME命令 + if ("TIME".equalsIgnoreCase(inputLine.trim())) { + String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + out.println("当前服务器时间: " + currentTime); + } else { + // 将收到的消息回显给客户端 + out.println("Echo: " + inputLine); + } } } catch (IOException e) { - System.err.println("处理客户端连接时发生错误: " + e.getMessage()); + System.err.println("处理客户端 " + clientSocket.getRemoteSocketAddress() + " 连接时发生错误: " + e.getMessage()); } finally { try { clientSocket.close(); - System.out.println("客户端连接已关闭"); + System.out.println("客户端 " + clientSocket.getRemoteSocketAddress() + " 连接已关闭"); } catch (IOException e) { - System.err.println("关闭客户端连接时发生错误: " + e.getMessage()); + System.err.println("关闭客户端 " + clientSocket.getRemoteSocketAddress() + " 连接时发生错误: " + e.getMessage()); } } } @@ -90,16 +112,19 @@ public class EchoServer { public static void main(String[] args) { EchoServer server = new EchoServer(); - try { - server.start(); - } catch (IOException e) { - System.err.println("启动服务器时发生错误: " + e.getMessage()); - } finally { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { + System.out.println("正在关闭服务器..."); server.stop(); } catch (IOException e) { System.err.println("停止服务器时发生错误: " + e.getMessage()); } + })); + + try { + server.start(); + } catch (IOException e) { + System.err.println("启动服务器时发生错误: " + e.getMessage()); } } } \ No newline at end of file -- Gitee