You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java客户端调用双服务器报错排查及多服务器通信实现指导

问题排查与解决方案:单客户端调用多服务器的Socket问题

我来帮你一步步拆解问题,先从代码里的明显错误入手,再给你梳理单客户端调用多服务器的正确实现方向。

一、你的代码中的核心错误

客户端代码的问题

  • 错误1:使用已关闭的Socket输入流
    在第一个Socket s 执行s.close()后,你写了Scanner sc3 = new Scanner(s.getInputStream());,但此时s已经被关闭,应该改用第二个Socket s2 的输入流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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:44:59