diff --git a/src/main/java/com/example/echo/EchoClient.java b/src/main/java/com/example/echo/EchoClient.java index 12bf370680d6c970c5ebc423893e79ebdf7fd2d5..781356b084ccc6c5f0b2b1e4bedc9c79a2d72955 100644 --- a/src/main/java/com/example/echo/EchoClient.java +++ b/src/main/java/com/example/echo/EchoClient.java @@ -9,7 +9,7 @@ import java.net.*; */ public class EchoClient { private static final String SERVER_HOST = "localhost"; - private static final int SERVER_PORT = 8888; + private static final int SERVER_PORT = 10086; public static void main(String[] args) { try (Socket socket = new Socket(SERVER_HOST, SERVER_PORT); diff --git a/src/main/java/com/example/echo/EchoServer.java b/src/main/java/com/example/echo/EchoServer.java index f55f684ccb91a654ac3993a691cb9ef5f97c0396..c1d8504bd348450ec912963aecd3d7b6539fffdf 100644 --- a/src/main/java/com/example/echo/EchoServer.java +++ b/src/main/java/com/example/echo/EchoServer.java @@ -1,91 +1,187 @@ package com.example.echo; +import javax.net.ServerSocketFactory; import java.io.*; import java.net.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; /** - * 单客户端Echo服务器 - * 这个服务器一次只处理一个客户端连接 + * 多客户端Echo服务器 + * 强化日志输出,直观体现多客户端同时访问 */ public class EchoServer { - private static final int PORT = 8888; + // 更换为不常用端口,避免冲突 + private static final int PORT = 10086; private ServerSocket serverSocket; private boolean running = false; private ExecutorService executor; + private static final int THREAD_POOL_SIZE = 10; + + // 核心:统计在线客户端(线程安全) + private final AtomicInteger clientCount = new AtomicInteger(0); + // 存储客户端信息(客户端标识 -> 连接时间),线程安全Map + private final Map onlineClients = new ConcurrentHashMap<>(); + // 客户端连接序号(唯一标识) + private final AtomicInteger clientSeq = new AtomicInteger(0); public EchoServer() { - executor = Executors.newSingleThreadExecutor(); + // 线程池命名,便于日志识别 + executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE, r -> { + Thread t = new Thread(r); + t.setName("Client-Handler-" + t.getId()); + return t; + }); } /** * 启动服务器 */ public void start() throws IOException { - serverSocket = new ServerSocket(PORT); + // 端口复用配置,避免重启端口占用 + ServerSocketFactory factory = ServerSocketFactory.getDefault(); + serverSocket = factory.createServerSocket(); + serverSocket.setReuseAddress(true); + serverSocket.bind(new InetSocketAddress(PORT)); + running = true; - System.out.println("Echo Server已启动,监听端口: " + PORT); + System.out.println("===== Echo Server 启动成功 ====="); + System.out.println("监听端口: " + PORT); + System.out.println("线程池大小: " + THREAD_POOL_SIZE); + System.out.println("===============================\n"); + + // 循环接受客户端连接 + while (running) { + System.out.println("\n【服务器状态】等待新客户端连接 | 当前在线客户端数: " + clientCount.get()); + Socket clientSocket = serverSocket.accept(); + + // 生成客户端唯一标识(序号+IP+端口) + int seq = clientSeq.incrementAndGet(); + InetSocketAddress clientAddress = (InetSocketAddress) clientSocket.getRemoteSocketAddress(); + String clientId = String.format("[客户端-%d][%s:%d]", + seq, clientAddress.getHostString(), clientAddress.getPort()); - // 等待客户端连接 - System.out.println("等待客户端连接..."); - Socket clientSocket = serverSocket.accept(); - System.out.println("客户端已连接: " + clientSocket.getRemoteSocketAddress()); + // 在线客户端计数+1 + int currentOnline = clientCount.incrementAndGet(); + // 记录客户端连接信息 + String connectTime = new SimpleDateFormat("HH:mm:ss").format(new Date()); + onlineClients.put(clientId, connectTime); - handleClient(clientSocket); + // 核心日志:新客户端连接,体现并发 + System.out.println("\n========== 新客户端连接 =========="); + System.out.println("客户端标识: " + clientId); + System.out.println("连接时间: " + connectTime); + System.out.println("处理线程: " + Thread.currentThread().getName()); + System.out.println("当前在线总数: " + currentOnline); + System.out.println("在线客户端列表: " + onlineClients.keySet()); + System.out.println("==================================="); + + // 提交到线程池处理(体现并发) + executor.submit(() -> handleClient(clientSocket, clientId)); + } } /** - * 处理客户端连接 + * 处理单个客户端的交互(每个客户端一个线程) */ - private void handleClient(Socket clientSocket) { + private void handleClient(Socket clientSocket, String clientId) { + // 获取当前处理线程信息 + String threadInfo = Thread.currentThread().getName() + "(ID:" + Thread.currentThread().getId() + ")"; + try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { - String inputLine; - System.out.println("开始与客户端通信..."); + System.out.printf("\n【并发处理】%s 开始交互 | 处理线程: %s\n", clientId, threadInfo); + out.println("欢迎连接Echo服务器!当前在线客户端数: " + clientCount.get()); + out.println("发送 'TIME' 获取系统时间 | 发送 'bye' 断开连接"); + String inputLine; + // 循环读取客户端消息 while ((inputLine = in.readLine()) != null) { - if ("bye".equalsIgnoreCase(inputLine.trim())) { - System.out.println("客户端请求断开连接"); + // 打印并发交互日志(核心:体现多客户端同时发消息) + System.out.printf("\n【并发消息】%s | 线程: %s | 收到消息: %s\n", + clientId, threadInfo, inputLine); + + // 处理TIME命令 + if ("TIME".equalsIgnoreCase(inputLine.trim())) { + String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + out.println("当前时间: " + currentTime); + System.out.printf("【响应】%s | 线程: %s | 返回时间: %s\n", + clientId, threadInfo, currentTime); + } + // 处理断开连接 + else if ("bye".equalsIgnoreCase(inputLine.trim())) { + out.println("再见!当前在线客户端数: " + (clientCount.get() - 1)); + System.out.printf("【主动断开】%s | 线程: %s | 客户端请求断开\n", + clientId, threadInfo); break; } - - System.out.println("收到消息: " + inputLine); - // 将收到的消息回显给客户端 - out.println("Echo: " + inputLine); + // 普通消息回显 + else { + out.println("Echo: " + inputLine); + System.out.printf("【回显响应】%s | 线程: %s | 已回显消息\n", + clientId, threadInfo); + } } } catch (IOException e) { - System.err.println("处理客户端连接时发生错误: " + e.getMessage()); + System.err.printf("\n【连接异常】%s | 线程: %s | 错误: %s\n", + clientId, threadInfo, e.getMessage()); } finally { + // 释放资源 try { - clientSocket.close(); - System.out.println("客户端连接已关闭"); + if (clientSocket != null && !clientSocket.isClosed()) { + clientSocket.close(); + } } catch (IOException e) { - System.err.println("关闭客户端连接时发生错误: " + e.getMessage()); + System.err.printf("【关闭失败】%s | 关闭Socket错误: %s\n", clientId, e.getMessage()); } + + // 在线计数-1 + int currentOnline = clientCount.decrementAndGet(); + // 移除在线客户端列表 + onlineClients.remove(clientId); + + // 核心日志:客户端断开,体现并发状态更新 + System.out.println("\n========== 客户端断开 =========="); + System.out.println("客户端标识: " + clientId); + System.out.println("处理线程: " + threadInfo); + System.out.println("当前在线总数: " + currentOnline); + System.out.println("剩余在线客户端: " + onlineClients.keySet()); + System.out.println("==================================="); } } /** * 停止服务器 */ - public void stop() throws IOException { + public void stop() { running = false; + // 关闭ServerSocket if (serverSocket != null && !serverSocket.isClosed()) { - serverSocket.close(); + try { + serverSocket.close(); + } catch (IOException e) { + System.err.println("关闭ServerSocket失败: " + e.getMessage()); + } } - executor.shutdown(); - try { - if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + // 关闭线程池 + if (executor != null && !executor.isShutdown()) { + executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { executor.shutdownNow(); + Thread.currentThread().interrupt(); } - } catch (InterruptedException e) { - executor.shutdownNow(); } - System.out.println("服务器已停止"); + System.out.println("\n===== Echo Server 已停止 ====="); + System.out.println("最终在线客户端数: " + clientCount.get()); } public static void main(String[] args) { @@ -94,12 +190,7 @@ public class EchoServer { server.start(); } catch (IOException e) { System.err.println("启动服务器时发生错误: " + e.getMessage()); - } finally { - try { - server.stop(); - } catch (IOException e) { - System.err.println("停止服务器时发生错误: " + e.getMessage()); - } + server.stop(); } } } \ No newline at end of file diff --git a/src/main/java/com/example/echo/com/example/echo/EchoServer.class b/src/main/java/com/example/echo/com/example/echo/EchoServer.class new file mode 100644 index 0000000000000000000000000000000000000000..612711668da1ee2f9d055525c3758d4d60e929ad Binary files /dev/null and b/src/main/java/com/example/echo/com/example/echo/EchoServer.class differ