
java 简单 socket 服务器默认在单次客户端会话结束后即终止;要实现长期运行,需将 accept() 和 i/o 处理逻辑置于无限循环中,并妥善处理异常与资源释放。
这是一个典型的“单连接 vs 持续服务”设计问题。你当前的 Java 服务器代码使用了 try-with-resources 语句,它会在 clientSocket 接收并处理完一次请求(readLine() 返回 null,表示输入流关闭)后自动关闭所有资源——包括 ServerSocket。因此,整个 try 块执行完毕,程序自然退出。
要让服务器持续运行、反复接受新连接,核心修改是:将 accept() 及其后续处理封装进一个外层 while (true) 循环中,并确保每次连接都在独立作用域内完成资源管理,避免因单个客户端异常导致整个服务器崩溃。
以下是改进后的 Java 服务器代码(关键改动已标注):
public class EchoServer {
public static void main(String[] args) {
int portNumber = 4444;
if (args.length > 0) {
try {
portNumber = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Invalid port: " + args[0]);
System.exit(1);
}
}
// 外层无限循环:持续等待新连接
while (true) {
ServerSocket serverSocket = null;
Socket clientSocket = null;
try {
serverSocket = new ServerSocket(portNumber);
System.out.println("Server listening on port " + portNumber);
// 阻塞等待客户端连接(每次循环只 accept 一个)
clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
// 为每个连接单独创建 I/O 流(不放在 try-with-resources 外层,避免关闭 serverSocket)
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
// 读取客户端发送的单行(注意:Go 客户端未发送换行符!见下方说明)
if ((inputLine = in.readLine()) != null) {
System.out.println("Received: " + inputLine);
out.println("Echo: " + inputLine); // 响应带前缀便于验证
} else {
System.out.println("Client closed connection immediately.");
}
} catch (IOException e) {
System.err.println("I/O error handling client: " + e.getMessage());
// 不中断循环,继续监听下一个连接
} finally {
// 安全关闭当前连接资源(clientSocket 和 streams),但保留 serverSocket 活跃
try {
if (clientSocket != null && !clientSocket.isClosed()) {
clientSocket.close();
}
if (serverSocket != null && !serverSocket.isClosed()) {
// 注意:此处不关闭 serverSocket,否则循环失效
// serverSocket.close(); // ❌ 错误:移除此行
}
} catch (IOException e) {
System.err.println("Error closing resources: " + e.getMessage());
}
}
}
// 注意:此行永不执行(死循环),如需优雅退出可添加 shutdown hook 或信号处理
}
}⚠️ 重要注意事项:
立即学习“Java免费学习笔记(深入)”;
-
Go 客户端需发送换行符:你当前的 Go 代码 conn.Write([]byte("hello world")) 发送的是无 \n 的原始字节,而 Java 的 BufferedReader.readLine() 会一直阻塞直到遇到行结束符(\n, \r\n)或流关闭。因此,服务器实际会卡在 readLine()。修复方式是在 Go 中发送带换行的消息:
conn.Write([]byte("hello world\n")) // ✅ 添加 \n或更推荐使用 fmt.Fprintln:
fmt.Fprintln(conn, "hello world") // 自动追加 \n 并 flush
-
并发支持(进阶):上述代码为 迭代式服务器(Iterative Server),一次只服务一个客户端。若需同时处理多个客户端,应为每个 clientSocket 启动一个新线程或使用 ExecutorService:
ExecutorService pool = Executors.newCachedThreadPool(); while (true) { Socket client = serverSocket.accept(); pool.submit(() -> handleClient(client)); // 将 I/O 逻辑抽离为方法 } 资源泄漏防护:务必在 finally 块中关闭 clientSocket,但绝不能关闭 serverSocket —— 它是整个服务的入口,关闭即终止监听。
总结:让 Java Socket 服务器持续运行的本质,是分离“监听套接字”(长期存活)与“服务套接字”(按需创建/销毁),并通过外层循环维持监听生命周期。配合正确的协议约定(如换行符),即可构建稳定的基础 TCP 服务。










