如何用BufferedReader读取Socket中所有可用字符到String?
解决方案:直接使用InputStream读取字节并转换为字符串
你的核心问题在于BufferedReader.readLine()依赖换行符才能返回,而且会一直阻塞直到换行符出现,这完全不符合你的数据格式要求。我们可以直接操作Socket的底层InputStream来读取字节,再转换为字符串,这样就能处理任意格式的数据流,完全不需要依赖换行符。
推荐实现:阻塞式读取(高效且可靠)
因为你已经为每个Socket单独创建了线程,阻塞式读取是更高效的选择——线程会在没有数据时自动挂起,不会空循环浪费CPU资源,直到有数据到达或连接断开。
修改后的完整代码如下:
thread = new Thread(() -> { try ( PrintWriter out = new PrintWriter(client.getOutputStream(), true); InputStream in = client.getInputStream() ) { // 定义缓冲区大小,可根据你的数据规模调整(比如4096或8192) byte[] buffer = new byte[1024]; int bytesRead; // 如果需要将所有数据合并成完整字符串再处理,可使用这个StringBuilder StringBuilder fullData = new StringBuilder(); while (true) { // read()会阻塞直到有数据可读,连接关闭时会返回-1 bytesRead = in.read(buffer); if (bytesRead == -1) { // 连接已正常关闭,退出循环 break; } // 将读取到的字节转换为字符串,务必指定编码(避免乱码) String chunk = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8); // 如果你需要每次收到数据就立即传给delegate: delegate.didReadData(this, chunk); // 如果你需要等连接关闭后,再把所有数据一次性传给delegate: fullData.append(chunk); } // 连接关闭后,传递完整数据(如果需要) // delegate.didReadData(this, fullData.toString()); } catch (IOException e) { delegate.didDropSocket(this); } }); thread.start();
关键点说明:
- 弃用BufferedReader:它的缓冲机制和
readLine()的换行依赖完全不适合你的场景,直接用InputStream读取字节更稳定可靠。 - 显式指定字符编码:转换字节到字符串时,一定要显式指定编码(比如
StandardCharsets.UTF_8),避免依赖平台默认编码导致乱码问题。 - 缓冲区大小调整:
byte[] buffer的大小可以根据你的数据平均长度灵活调整,平衡内存占用和读取效率。 - 连接关闭判断:当
read()返回-1时,表示Socket连接已正常关闭,此时可以退出循环并处理积累的完整数据(如果需要)。
可选:非阻塞式读取(基于ready()检查)
如果你确实需要非阻塞的方式(不希望线程挂起),可以用in.ready()判断是否有数据可读,再读取当前所有可用字节。但注意这种方式需要加短暂睡眠,避免空循环占用过多CPU:
thread = new Thread(() -> { try ( PrintWriter out = new PrintWriter(client.getOutputStream(), true); InputStream in = client.getInputStream() ) { StringBuilder fullData = new StringBuilder(); while (true) { if (in.ready()) { int availableBytes = in.available(); byte[] buffer = new byte[availableBytes]; int bytesRead = in.read(buffer); if (bytesRead == -1) { break; } String chunk = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8); delegate.didReadData(this, chunk); fullData.append(chunk); } // 短暂睡眠,减少空循环的CPU消耗 Thread.sleep(10); } // delegate.didReadData(this, fullData.toString()); } catch (IOException | InterruptedException e) { delegate.didDropSocket(this); } }); thread.start();
不过这种方式不如阻塞式可靠:in.ready()返回false不代表没有数据,只是下一次read()可能会阻塞,所以可能会错过部分数据的读取时机。因此更推荐使用第一种阻塞式方案。
内容的提问来源于stack exchange,提问作者mag_zbc
相关产品推荐
相关产品推荐

