Java InputStream自动拆分socket消息

Java InputStream automatically splits socket messages

我在 Java 有一个非常奇怪的行为,我不知道这是故意的还是偶然的。

我确实有一个到服务器的套接字连接,它向我发送对请求的响应。我正在使用以下循环从 Socket 读取此响应,该循环封装在 try-with-resource 中。

BufferedInputStream remoteInput = new BufferedInputStream(remoteSocket.getInputStream())
final byte[] response = new byte[512];
int bytes_read;
while ((bytes_read = remoteInput.read(response,0,response.length)) != -1) {
    // Messageparsingstuff which does not affect the behaviour
}

根据我的理解,"read" 方法将尽可能多的字节填充到字节数组中。限制因素是接收到的字节数或数组的大小。

不幸的是,这不是正在发生的事情:我正在传输的协议用几个较小的答案来回答我的请求,这些答案是通过同一个套接字连接一个接一个地发送的。

在我的例子中,"read" 方法总是 returns 与数组中那些较小的答案之一。答案的长度各不相同,但适合数组的 512 字节总是足够的。这意味着我的数组始终只包含一条消息,并且数组的 rest/unneeded 部分保持不变。

如果我有意定义比我的消息更小的字节数组,它将 return 几个完全填充的数组和最后一个包含其余字节的数组,直到消息完成。

(一个 100 字节的答案,数组长度为 30 returns 三个完全填充的数组,一个只使用了 10 个字节)

InputStream 或套接字连接通常不应以任何方式解释传输的字节,这就是我现在非常困惑的原因。我的程序不知道以任何方式使用的协议。其实我的整个程序就是这个循环和建立套接字连接所需要的东西。

如果我可以依靠这种行为,那么解析响应会变得非常容易,但由于我不知道是什么导致了这种行为,所以我不知道我是否可以依靠它。

我传输的协议是 LDAP,但由于我的程序完全不知道这一点,所以这无关紧要。

根据我的理解,"read" 方法将尽可能多的字节填充到字节数组中。

您的理解有误。该方法 returning "number of bytes read" 的全部要点是:它 可能 return 任何数字。准确地说:在谈论 blocking 读取时 - 当方法 returns 时,它读取了 something;因此它将 return 一个数字 >= 1.

换句话说:你应该从不每次都依赖read()读取特定数量的字节。您总是总是总是 检查 returned 号码;如果您正在等待达到某个值,那么 必须在您的代码中对此做一些事情(比如再次缓冲;直到您的 "enough" 字节自己的缓冲区继续)。

事实是:此类读取操作涉及整个巨大的元素堆栈。网络、操作系统、jvm。您无法控制到底发生了什么;因此你不能也不应该像这样在你的代码中构建任何隐含的假设。

虽然您可能会在给定的机器上看到这种行为,尤其是环回,但一旦您开始使用真实网络并使用不同的硬件,这种情况就会改变。

如果您发送消息时有足够的延迟,并且阅读速度足够快,您将一次看到一条消息。但是,如果写消息发送得足够近或者您的 reader 以任何方式延迟,您可以一次发送多条消息。

此外,如果您的消息足够大,例如大约 MTU 或更多,即使您的缓冲区足够大,也可以分解单个消息。