BufferedReader.read(char[])何时返回-1?Evolis SDK通信读取阻塞问题
首先直接回答你的核心疑问:BufferedReader.read(char[]) 会在输入流的末尾被抵达时返回-1——换句话说,只有当对方(这里是Evolis SDK的服务端)主动关闭了Socket连接的输出端,或者整个Socket连接被断开时,这个方法才会返回-1。
为什么你的代码会卡住?
你现在的问题在于:服务端返回完整的JSON响应后,并没有关闭Socket连接(SDK大概率采用长连接模式,允许后续继续发送请求)。所以你的while((br.read(data)) != -1)循环在第一次读取到响应后,会一直阻塞等待下一段数据,直到连接超时,这完全符合TCP Socket的行为逻辑。
另外还要提一句:你原代码里answer = new String(data);存在小问题——如果读取到的字符数小于data数组的长度,数组里未被覆盖的位置会残留旧数据,应该用new String(data, 0, bytesRead)来只取实际读取到的内容。
如何修复这个问题?
因为JSON-RPC的响应是一个完整的JSON对象,我们不需要一直读取到流结束,而是读取到完整的JSON响应就可以停止循环。这里提供两种可行的修改思路:
思路1:基于JSON结构判断读取结束
可以先把读取到的字符拼接起来,然后尝试判断是否已获取完整的JSON对象(复杂场景建议用专业JSON库解析,简单场景可以通过括号匹配判断):
// 替换原来的while循环部分 StringBuilder responseBuilder = new StringBuilder(); char[] buffer = new char[1024]; int bytesRead; while ((bytesRead = br.read(buffer)) != -1) { responseBuilder.append(buffer, 0, bytesRead); // 简单判断是否是完整的JSON对象(仅适用于单层结构,复杂结构建议用Jackson/Gson等库解析) String tempResponse = responseBuilder.toString(); int openBraces = (int) tempResponse.chars().filter(c -> c == '{').count(); int closeBraces = (int) tempResponse.chars().filter(c -> c == '}').count(); if (tempResponse.startsWith("{") && openBraces == closeBraces) { answer = tempResponse; break; // 拿到完整响应,退出循环 } } System.out.println(answer);
思路2:检查SDK的通信协议规范
很多基于TCP的JSON-RPC实现会在响应前带上长度前缀(比如先发送响应的字节数,再发送JSON内容)。你可以查看Evolis SDK的官方文档,确认是否有这样的约定。如果有,就可以先读取长度值,再读取对应长度的字节,精准获取完整响应,避免阻塞。
思路3:单次请求后主动关闭输出端
如果你的场景只是单次请求-响应,也可以在发送请求后,主动关闭输出流的写入端,告诉服务端你不会再发送数据了。部分服务端收到这个信号后,会返回响应并关闭连接,这样你的read方法就会在读完响应后返回-1:
out.write(byteRequest); out.flush(); out.shutdownOutput(); // 告诉服务端已发送完请求,不会再写数据 // 读取响应 StringBuilder responseBuilder = new StringBuilder(); char[] buffer = new char[1024]; int bytesRead; while ((bytesRead = br.read(buffer)) != -1) { responseBuilder.append(buffer, 0, bytesRead); } answer = responseBuilder.toString(); System.out.println(answer);
内容的提问来源于stack exchange,提问作者Tobia

