diff --git a/README.md b/README.md index 4b3544a761bee35113af9e5177c894f96dd8b441..3d3bbfa3f165ed1299120fe698098ead0429cd74 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ mvn clean compile ### 2. 运行服务器 ```bash -mvn exec:java -Dexec.mainClass="com.example.echo.EchoServer" +mvn exec:java "-Dexec.mainClass=com.example.echo.EchoServer" ``` 或直接运行: @@ -56,7 +56,7 @@ java -cp target/classes com.example.echo.EchoServer 另开一个终端,运行: ```bash -mvn exec:java -Dexec.mainClass="com.example.echo.EchoClient" +mvn exec:java "-Dexec.mainClass=com.example.echo.EchoClient" ``` 或直接运行: @@ -76,4 +76,58 @@ java -cp target/classes com.example.echo.EchoClient ## 测试 -输入任意消息到客户端,服务器将回传相同的消息加上"Echo: "前缀。输入"bye"可断开连接。 \ No newline at end of file +输入任意消息到客户端,服务器将回传相同的消息加上"Echo: "前缀。输入"bye"可断开连接。 + + + +如果删除 clientSocket.close(),会出现以下问题: +主要问题 +资源泄漏 +客户端套接字连接不会被正确关闭 +系统文件描述符资源会被持续占用 +长时间运行可能导致服务器资源耗尽 +连接状态异常 +客户端连接会保持在ESTABLISHED状态 +无法正常释放TCP连接资源 +可能导致连接超时后才被动关闭 +服务器性能影响 +每个未关闭的连接都会占用服务器内存 +连接数达到系统限制时,新客户端将无法连接 +影响服务器处理其他客户端的能力 + + +更高效的替代方案 +1. 线程池模型 +// 使用固定大小的线程池处理客户端连接 + ExecutorService threadPool = Executors.newFixedThreadPool(100); + + 优势: + 限制线程数量,避免资源耗尽 + 复用线程减少创建开销 + 更好的资源控制 +2. I/O多路复用(NIO) + // 使用Selector实现单线程处理多个连接 + Selector selector = Selector.open(); + ServerSocketChannel serverChannel = ServerSocketChannel.open(); + + 优势: + 单线程可处理成千上万连接 + 零拷贝技术提高性能 + 更少的系统资源占用 + +3. 异步I/O(AIO) + // 使用异步套接字通道 + AsynchronousServerSocketChannel serverChannel = + AsynchronousServerSocketChannel.open(); + + 优势: + 事件驱动,无需主动轮询 + 系统级异步支持 + 高的并发处理能力 + + 4. Reactor模式 + 结合NIO和线程池 + 主线程负责I/O操作 + 工作线程处理业务逻辑 + 经典实现如Netty框架 + 对于高并发场景,建议采用NIO或成熟的网络框架如Netty,而不是为每个连接创建独立线程。 \ No newline at end of file diff --git a/src/main/java/com/example/echo/EchoClient.class b/src/main/java/com/example/echo/EchoClient.class new file mode 100644 index 0000000000000000000000000000000000000000..33e830a9badecb35d85d63f86a4c166a78e073d1 Binary files /dev/null and b/src/main/java/com/example/echo/EchoClient.class differ diff --git a/src/main/java/com/example/echo/EchoServer.class b/src/main/java/com/example/echo/EchoServer.class new file mode 100644 index 0000000000000000000000000000000000000000..74eab282b2b2559f0d22a60fb775e4607b6d2a64 Binary files /dev/null and b/src/main/java/com/example/echo/EchoServer.class differ diff --git a/src/main/java/com/example/echo/EchoServer.java b/src/main/java/com/example/echo/EchoServer.java index f55f684ccb91a654ac3993a691cb9ef5f97c0396..b303a271d9d23ba54b3d41078700ef57d8e7f206 100644 --- a/src/main/java/com/example/echo/EchoServer.java +++ b/src/main/java/com/example/echo/EchoServer.java @@ -2,13 +2,14 @@ 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 +18,8 @@ public class EchoServer { private ExecutorService executor; public EchoServer() { - executor = Executors.newSingleThreadExecutor(); + // 使用固定线程池来处理多个客户端连接 + executor = Executors.newFixedThreadPool(10); } /** @@ -28,12 +30,21 @@ 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 { + System.out.println("等待客户端连接..."); + Socket clientSocket = serverSocket.accept(); + System.out.println("客户端已连接: " + clientSocket.getRemoteSocketAddress()); + + // 为每个客户端创建一个新的任务 + executor.submit(() -> handleClient(clientSocket)); + } catch (IOException e) { + if (running) { + System.err.println("接受客户端连接时发生错误: " + e.getMessage()); + } + } + } } /** @@ -44,27 +55,35 @@ public class EchoServer { PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { String inputLine; - System.out.println("开始与客户端通信..."); + System.out.println("开始与客户端 " + clientSocket.getRemoteSocketAddress() + " 通信..."); while ((inputLine = in.readLine()) != null) { if ("bye".equalsIgnoreCase(inputLine.trim())) { - System.out.println("客户端请求断开连接"); + System.out.println("客户端 " + clientSocket.getRemoteSocketAddress() + " 请求断开连接"); break; } - System.out.println("收到消息: " + inputLine); - // 将收到的消息回显给客户端 - out.println("Echo: " + inputLine); + System.out.println("收到来自客户端 " + clientSocket.getRemoteSocketAddress() + " 的消息: " + 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()); } } } @@ -78,28 +97,23 @@ public class EchoServer { serverSocket.close(); } executor.shutdown(); - try { - if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { - executor.shutdownNow(); - } - } catch (InterruptedException e) { - executor.shutdownNow(); - } System.out.println("服务器已停止"); } 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 { 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