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

Socket电影评分程序:服务端收不到消息及阻塞问题排查

问题排查与修复方案

核心问题分析

1. 客户端阻塞导致后续代码完全无法执行

客户端内层的while(line!=null)循环是致命逻辑错误:fromServer.readLine()方法在服务端未关闭输出流、且没有新数据发送时,会一直阻塞等待,而不是返回null。当服务端发送完"Pick a movie:"后暂停输出,客户端的line=fromServer.readLine()会永久卡住,导致toServer.println("ll")、电影名输入循环等后续代码永远得不到执行机会。

2. 服务端电影列表加载逻辑失效

服务端读取Movies.txt的流只会初始化一次,文件读完后,后续客户端连接时,while((line=br.readLine())!=null)循环直接跳过,不会向客户端发送任何电影列表。同时服务端仅读取一次客户端消息就回到外层循环等待新连接,无法处理当前客户端的持续交互。

3. 客户端电影名验证初始值错误

客户端初始化String movie=" ";,空格字符串不可能出现在电影列表中,导致初始的!list.contains(movie)必然为true,若用户输入无效电影名,会陷入无限循环。


修复后的代码

服务端代码

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class Server {
    public static int test_port = 12345;
    private int port;
    public static HashMap<String, Double> listOfMovies = new HashMap<>();
    public static HashMap<String, Integer> counter = new HashMap<>();

    public static void main(String[] args) {
        Server server = new Server(Server.test_port);
        server.execute();
    }

    private void execute() {
        try (ServerSocket s = new ServerSocket(port)) {
            System.out.println("Server started on port " + port);
            while (true) {
                Socket client = s.accept();
                System.out.println("Client connected!");
                
                // 为每个客户端创建独立的通信流
                BufferedReader fromUser = new BufferedReader(new InputStreamReader(client.getInputStream()));
                PrintWriter toUser = new PrintWriter(client.getOutputStream(), true);

                // 每次客户端连接时重新加载电影列表,确保拿到最新数据
                reloadMovieList();

                // 发送欢迎信息与电影列表
                toUser.println("Welcome!");
                toUser.println("Available movies to rate:");
                for (String movie : listOfMovies.keySet()) {
                    toUser.println(movie);
                }
                toUser.println("Pick a movie:");

                // 持续处理客户端的交互请求
                String clientMsg;
                while ((clientMsg = fromUser.readLine()) != null) {
                    System.out.println("Received from client: " + clientMsg);
                    if (clientMsg.equalsIgnoreCase("exit")) {
                        toUser.println("Goodbye!");
                        break;
                    }
                    if (listOfMovies.containsKey(clientMsg)) {
                        toUser.println("Enter your rating (0-5):");
                        String ratingStr = fromUser.readLine();
                        try {
                            double rating = Double.parseDouble(ratingStr);
                            int count = counter.get(clientMsg);
                            double total = listOfMovies.get(clientMsg) * count;
                            total += rating;
                            count++;
                            listOfMovies.put(clientMsg, total / count);
                            counter.put(clientMsg, count);
                            toUser.println("Thanks for rating! Current average for " + clientMsg + " is " + String.format("%.1f", listOfMovies.get(clientMsg)));
                        } catch (NumberFormatException e) {
                            toUser.println("Invalid rating! Please enter a number between 0-5.");
                        }
                    } else {
                        toUser.println("Movie not found! Pick again from the list:");
                    }
                    toUser.println("Pick another movie (or type 'exit' to quit):");
                }
                System.out.println("Client disconnected.");
                client.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 独立的电影列表加载方法,每次调用都会重新读取文件
    private void reloadMovieList() throws IOException {
        listOfMovies.clear();
        counter.clear();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Movies.txt")))) {
            String line;
            while ((line = br.readLine()) != null) {
                listOfMovies.put(line, 0.0);
                counter.put(line, 0);
            }
        }
    }

    public Server(int port) {
        this.port = port;
    }
}

客户端代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Scanner;

public class Client {
    private String hostname;
    private int port;
    BufferedReader fromServer;
    PrintWriter toServer;
    ArrayList<String> list = new ArrayList<String>();

    public static void main(String[] args) {
        Client client = new Client("localhost", Server.test_port);
        client.execute();
    }

    private void execute() {
        try (Socket client = new Socket(this.hostname, this.port);
             Scanner sc = new Scanner(System.in)) {
            System.out.println("Client connected to server: " + this.hostname);
            fromServer = new BufferedReader(new InputStreamReader(client.getInputStream()));
            toServer = new PrintWriter(client.getOutputStream(), true);

            String line;
            // 读取服务端消息,直到收到"Pick a movie:"标记,避免无限阻塞
            while ((line = fromServer.readLine()) != null) {
                System.out.println(line);
                if (line.equals("Pick a movie:")) {
                    break;
                }
                // 过滤非电影行,收集有效电影列表
                if (!line.startsWith("Welcome!") && !line.startsWith("Available movies")) {
                    list.add(line);
                }
            }

            // 交互循环,支持多次评分与退出
            while (true) {
                System.out.print("Enter movie name (or 'exit' to quit): ");
                String movie = sc.nextLine().trim();
                if (movie.equalsIgnoreCase("exit")) {
                    toServer.println(movie);
                    break;
                }
                if (list.contains(movie)) {
                    toServer.println(movie);
                    // 读取服务端的回复并处理
                    String response = fromServer.readLine();
                    System.out.println(response);
                    if (response.startsWith("Enter your rating")) {
                        String rating = sc.nextLine().trim();
                        toServer.println(rating);
                        String result = fromServer.readLine();
                        System.out.println(result);
                    }
                } else {
                    System.out.println("Movie not in list! Please try again.");
                }
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Client(String hostname, int port) {
        this.hostname = hostname;
        this.port = port;
    }
}

关键修复说明

  1. 解决客户端阻塞:将无限读取循环改为读取到特定标记("Pick a movie:")就停止,确保后续代码能正常执行。
  2. 修复服务端列表加载:新增reloadMovieList方法,每次客户端连接时重新读取电影文件,避免流耗尽导致的列表为空问题。
  3. 支持持续交互:服务端增加循环处理客户端消息,可响应多次评分请求,不再处理一次就等待新连接。
  4. 优化输入逻辑:客户端调整电影名输入的初始值,增加退出选项,避免无效输入的无限循环。

内容的提问来源于stack exchange,提问作者Broj 1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:22:07