Java Socket读取JSON对象无异常无预期输出问题求解
问题根因
- 读消息的循环终止条件错误:
BufferedReader.readLine()仅在对端关闭连接、输入流到达末尾时才会返回null。常规C/S长连接场景下,Socket不会在发送完单条消息后主动断开,你的代码会一直阻塞在while循环中等待流结束,永远不会执行后续的JSON解析、字段打印逻辑,这就是无异常也无输出的核心原因。 - 未定义明确的消息边界:发送端直接将JSON序列化后的字符串写入输出流,没有添加任何分隔标识,即使修正循环逻辑,TCP传输的粘包/拆包问题会导致多条JSON拼接、单条JSON被拆分的情况,直接触发JSON解析失败。
- 额外的坏实践:每次发送消息都新建
PrintWriter包装流,既会产生不必要的性能开销,反复创建包装流还可能导致缓冲区数据异常。
修复方案
核心思路是用换行符作为单条JSON消息的边界(常规JSONObject.toString()输出的压缩格式JSON本身不含换行符,不会和边界标记冲突),发送端每条JSON末尾输出换行,接收端每读到一行完整内容就直接解析,不需要等流结束。
修正后的发送端代码
连接建立时初始化一次输出流,整个连接生命周期复用,不要每次发消息都新建:
// 连接初始化时创建输出流,全局复用 private PrintWriter printWriter; public void initStream() throws IOException { // 显式指定UTF-8编码,避免不同系统默认编码不一致导致乱码 printWriter = new PrintWriter( new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8) ); } public void writeMessage(JSONObject json) { // 用println输出,自动在JSON末尾添加换行符作为消息边界 printWriter.println(json.toString()); printWriter.flush(); }
修正后的接收端代码
每读到一行内容就直接解析为JSON,不需要循环拼接所有读到的内容:
private static void readMessageFromServer(Socket socket) { // 同样显式指定UTF-8编码,和发送端保持一致 try (BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8) )) { String readLine; // 读到一行就处理一行,每行对应一个完整JSON while ((readLine = br.readLine()) != null) { // 跳过传输中可能产生的空行 if (readLine.isBlank()) continue; JSONObject js = new JSONObject(readLine); // 直接用JSONObject提供的getString方法取值,比手动强转更安全 String action1 = js.getString("action1"); System.out.println(action1); } } catch (IOException e) { System.out.println("连接异常:" + e.getMessage()); } }
扩展注意事项
- 上述换行分隔的方案仅适用于传输压缩格式、无内部换行的JSON。如果需要传输格式化后带换行的JSON内容,请改用「长度前缀+内容」的自定义协议:先发送4字节整数标记后续JSON内容的字节长度,再发送对应长度的JSON字节;接收端先读取长度值,再读取满对应长度的字节后解析,从根本上避免粘包问题。
- 所有IO流的包装器(PrintWriter、BufferedReader等)都要在Socket连接建立时初始化一次复用,不要每次读写都新建。
- 收发两端必须显式指定相同的字符集,禁止依赖操作系统默认编码,否则跨环境运行时大概率出现乱码。
内容的提问来源于stack exchange,提问作者Lunarr
相关产品推荐
相关产品推荐

