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; } }
关键修复说明
- 解决客户端阻塞:将无限读取循环改为读取到特定标记("Pick a movie:")就停止,确保后续代码能正常执行。
- 修复服务端列表加载:新增
reloadMovieList方法,每次客户端连接时重新读取电影文件,避免流耗尽导致的列表为空问题。 - 支持持续交互:服务端增加循环处理客户端消息,可响应多次评分请求,不再处理一次就等待新连接。
- 优化输入逻辑:客户端调整电影名输入的初始值,增加退出选项,避免无效输入的无限循环。
内容的提问来源于stack exchange,提问作者Broj 1
相关产品推荐
相关产品推荐

