/** * 从 buf 中查找 "\r\n",并返回这段(包括 \r\n)空间的长度。 * 如果 buf 中没有,则返回 0 。 */ private static int findLineEnd(final byte[] buf, int rlen) { int splitbyte = 0; while (splitbyte + 1 < rlen) { if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 1 < rlen) { return splitbyte + 2; } splitbyte++; } return 0; } /** * 半包、粘包问题解决: * 从 InputStream 中找一行数据并返回。「一行」的标志是截止到 \r\n,不包括 \r\n 。 */ private static String readLine(InputStream inputStream) throws IOException, RuntimeException { int splitbyte = 0; int rlen = 0; byte[] buf = new byte[BUFSIZE]; int read = -1; inputStream.mark(BUFSIZE); try { read = inputStream.read(buf, 0, BUFSIZE); } catch (Exception e) { throw new SocketException("Could not Read"); } if (read == -1) { throw new SocketException("Could not Read"); } /* buf 中存储的数据区间为: buf[0] ... buf[read-1],共 read 个字节。 */ while (read > 0) { rlen += read; splitbyte = findLineEnd(buf, rlen); if (splitbyte > 0) { break; } read = inputStream.read(buf, rlen, BUFSIZE - rlen); } /* buf 中存储的数据区间为: buf[0] ... buf[splitbyte-1],共 splitbyte 个字节。其中包含 \r\n 在内。*/ // -2 的目的是返回的结果内容中不包含 \r\n 。 return new String(buf, 0, splitbyte - 2); } private static void safeClose(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { System.out.println("Could not close: " + e.getMessage()); } } private static void safeClose(Socket socket) { try { if (socket != null && !socket.isClosed()) { socket.close(); } } catch (IOException e) { throw new IllegalArgumentException("Cloud not close socket: " + e.getMessage()); } } private static void safeClose(ServerSocket serverSocket) { try { if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } } catch (IOException e) { throw new IllegalArgumentException("Cloud not close socket: " + e.getMessage()); } }
测试:
String message = "hello world\r\ngoodbye"; byte[] bytes = message.getBytes(StandardCharsets.UTF_8); //System.out.println(findLineEnd(bytes, bytes.length)); InputStream stream = new ByteArrayInputStream(bytes); String line = readLine(stream); System.out.println(line);