Java客户端调用双服务器报错排查及多服务器通信实现指导
问题排查与解决方案:单客户端调用多服务器的Socket问题
我来帮你一步步拆解问题,先从代码里的明显错误入手,再给你梳理单客户端调用多服务器的正确实现方向。
一、你的代码中的核心错误
客户端代码的问题
- 错误1:使用已关闭的Socket输入流
在第一个Sockets执行s.close()后,你写了Scanner sc3 = new Scanner(s.getInputStream());,但此时s已经被关闭,应该改用第二个Sockets2的输入流s2.getInputStream(),这会直接引发IO异常。 - 错误2:复用已关闭的Scanner
你先关闭了sc.close(),后续又调用number = sc.nextInt();,已关闭的Scanner无法再读取输入,会抛出IllegalStateException。 - 错误3:结果打印的变量混淆
最后打印System.out.println(temp);应该改为System.out.println(more);,否则你看不到第二个服务器的返回结果。 - 错误4:资源管理不规范
没有使用try-with-resources语法,手动关闭资源容易遗漏,导致Socket端口未被正确释放,这很可能是你遇到Address already in use: JVM_Bind的原因之一。
服务器端代码的问题
- 错误1:单连接即终止服务
你的Server1和Server2都是处理完一个连接就结束main方法,ServerSocket随之关闭,无法接受后续客户端请求,再次连接会直接失败。 - 错误2:资源泄漏
服务器端的Socket、流没有正确关闭,同样可能导致端口占用问题。
二、修复后的示例代码
修复后的客户端(顺序调用版本)
import java.io.IOException; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { Scanner userInput = new Scanner(System.in); // 调用Server1(端口1342) try (Socket socket1 = new Socket("localhost", 1342); Scanner serverInput1 = new Scanner(socket1.getInputStream()); PrintStream serverOutput1 = new PrintStream(socket1.getOutputStream())) { System.out.print("输入要发送给Server1的数字:"); int num1 = userInput.nextInt(); serverOutput1.println(num1); int result1 = serverInput1.nextInt(); System.out.println("Server1返回结果:" + result1); } catch (IOException e) { System.err.println("连接Server1时出错:" + e.getMessage()); } // 调用Server2(端口1343) try (Socket socket2 = new Socket("localhost", 1343); Scanner serverInput2 = new Scanner(socket2.getInputStream()); PrintStream serverOutput2 = new PrintStream(socket2.getOutputStream())) { System.out.print("输入要发送给Server2的数字:"); int num2 = userInput.nextInt(); serverOutput2.println(num2); int result2 = serverInput2.nextInt(); System.out.println("Server2返回结果:" + result2); System.out.println("最终结果:Server1=" + result1 + ", Server2=" + result2); } catch (IOException e) { System.err.println("连接Server2时出错:" + e.getMessage()); } userInput.close(); } }
修复后的服务器(支持多连接)
Server1.java
import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class Server1 { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(1342)) { System.out.println("Server1已启动,监听端口1342..."); // 循环接受客户端连接,支持多请求 while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("新客户端连接到Server1"); // 用线程处理每个请求,避免阻塞主线程 new Thread(() -> { try (Scanner clientInput = new Scanner(clientSocket.getInputStream()); PrintStream clientOutput = new PrintStream(clientSocket.getOutputStream())) { int number = clientInput.nextInt(); int result = number * 2; clientOutput.println(result); } catch (IOException e) { System.err.println("处理客户端请求出错:" + e.getMessage()); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } catch (IOException e) { System.err.println("Server1启动失败:" + e.getMessage()); } } }
Server2.java(仅计算逻辑不同)
import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class Server2 { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(1343)) { System.out.println("Server2已启动,监听端口1343..."); while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("新客户端连接到Server2"); new Thread(() -> { try (Scanner clientInput = new Scanner(clientSocket.getInputStream()); PrintStream clientOutput = new PrintStream(clientSocket.getOutputStream())) { int number = clientInput.nextInt(); int result = number * 10; clientOutput.println(result); } catch (IOException e) { System.err.println("处理客户端请求出错:" + e.getMessage()); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } catch (IOException e) { System.err.println("Server2启动失败:" + e.getMessage()); } } }
三、单客户端调用多服务器的实现方向
1. 顺序调用(同步)
就像上面的修复示例,先调用第一个服务器,等待返回结果后再调用第二个。这种方式逻辑简单清晰,适合对响应时间要求不高的场景,调试起来也更方便。
2. 并行调用(异步)
如果要模拟旅行社同时查询两家航空公司的场景,用并行调用能大幅缩短等待时间。可以用Java的CompletableFuture或者多线程同时发起请求,等所有结果返回后再比较价格。
示例(CompletableFuture实现并行查询):
import java.io.IOException; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; import java.util.concurrent.CompletableFuture; public class ParallelClient { public static void main(String[] args) { Scanner userInput = new Scanner(System.in); System.out.print("输入飞行里程:"); int miles = userInput.nextInt(); // 并行调用两个服务器 CompletableFuture<Integer> dreamLineFuture = CompletableFuture.supplyAsync(() -> callAirlineServer(1342, miles)); CompletableFuture<Integer> cloudLineFuture = CompletableFuture.supplyAsync(() -> callAirlineServer(1343, miles)); // 等待所有结果返回 CompletableFuture.allOf(dreamLineFuture, cloudLineFuture).join(); try { int dreamPrice = dreamLineFuture.get(); int cloudPrice = cloudLineFuture.get(); System.out.println("DreamLine报价:" + dreamPrice + "€"); System.out.println("CloudLine报价:" + cloudPrice + "€"); System.out.println("最便宜的航班来自:" + (dreamPrice < cloudPrice ? "DreamLine" : "CloudLine")); } catch (Exception e) { System.err.println("获取报价出错:" + e.getMessage()); } userInput.close(); } private static Integer callAirlineServer(int port, int miles) { try (Socket socket = new Socket("localhost", port); Scanner serverInput = new Scanner(socket.getInputStream()); PrintStream serverOutput = new PrintStream(socket.getOutputStream())) { serverOutput.println(miles); return serverInput.nextInt(); } catch (IOException e) { System.err.println("连接航空公司服务器(端口" + port + ")出错:" + e.getMessage()); return Integer.MAX_VALUE; // 返回极大值表示该服务器不可用 } } }
3. 避免Socket问题的关键要点
- 强制使用try-with-resources:自动关闭Socket、流等资源,彻底避免资源泄漏导致的端口占用。
- 服务器用线程处理连接:让服务器能同时响应多个客户端请求,不会因单个连接结束而停止服务。
- 保证端口唯一性:每个服务器必须监听不同的端口,避免端口冲突。
- 端口快速复用(可选):如果还是遇到端口占用问题,可以在创建ServerSocket时设置
SO_REUSEADDR选项:ServerSocket serverSocket = new ServerSocket(); serverSocket.setReuseAddress(true); serverSocket.bind(new InetSocketAddress(1342));
内容的提问来源于stack exchange,提问作者Mischa
相关产品推荐
相关产品推荐

