【问题标题】:Java InputStream automatically splits socket messagesJava InputStream 自动拆分套接字消息
【发布时间】:2017-04-24 08:45:41
【问题描述】:

我在 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
}

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

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

在我的情况下,“读取”方法总是返回数组中那些较小的答案之一。答案的长度各不相同,但适合数组的 512 字节总是足够的。这意味着我的数组始终只包含一条消息,并且数组的其余/不需要的部分保持不变。

如果我故意将字节数组定义为小于我的消息,它将返回几个完全填充的数组和一个包含其余字节的最后一个数组,直到消息完成。

(数组长度为 30 的 100 字节答案返回三个完全填充的数组和一个仅使用 10 个字节的数组)

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

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

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

【问题讨论】:

  • 我喜欢 Tmesus 这个词,它是英语中唯一以 tm.... 开头的词 ;)
  • 我什至不知道这个词。有趣,你可以在这里学到什么。
  • 我什至不知道这个词。我使用了“tmesis”这个词,并在其中添加了拉丁男性单数后缀。
  • @GhostCat 你有什么特别的原因让你删掉我最后一句以及我在帖子中的“签名”吗?我没有生气或任何事情。只是好奇这是否是我应该首先避免的事情。
  • 也许我应该在这样做的时候告诉你。事情是:“聊天”是给cmets的。或者换句话说:问题或答案中没有“称呼”(例如参见here)。换句话说:这不是什么大事,但你知道,我们在那里是非常认真的人,我们甚至彼此都不是很好……为了记录:这不完全是我喜欢的政策;但好吧,这是非正式的事情。

标签: java sockets ldap tcp-ip


【解决方案1】:

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

你的理解不正确。该方法返回“读取的字节数”的重点是:它可能返回任何数字。准确地说:当谈到 blocking 读取时 - 当方法返回时,它已经读取了 something;因此它将返回一个 >= 1 的数字。

换句话说:您应该从不每次都依赖read() 读取特定数量的字节。你总是总是检查返回的数字;如果您正在等待达到某个值,那么 必须在您的代码中对此进行处理(例如再次缓冲;直到您自己的缓冲区中有“足够”的字节才能继续) .

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

【讨论】:

  • 你可以保证至少一个字节被阻塞读取,除非连接已经丢失。
  • 有道理。相应更新。
【解决方案2】:

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

如果您发送消息的延迟足够长,并且阅读速度足够快,您一次只能看到一条消息。但是,如果写消息发送得足够近,或者您的阅读器有任何延迟,您可能会同时收到多条消息。

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

【讨论】:

  • 其实我在一个真实的网络上工作。我还故意放慢了循环速度,并且各个消息的长度变化很大。我认为这不太可能是偶然发生的。但我明白依赖它不是一个好主意。
  • @Tmesus 尝试发送 1 MB 并尽可能快地读取。您应该能够看到每个数据包的数据。
  • 我在 1999 年的第一份真正的工作是实现一个与 C++ 驱动的数据库后端对话的 Java GUI。 C++ 端不断通过套接字发送 CSV 数据。然后我 forgot 检查读取的字节数;我只是假设 4 字节的标头会一直存在。这在大多数情况下都有效;但是当我们的客户测试东西时,每周都会失败一次。花了 几周 才发现这个错误。
  • @GhostCat 我也做过类似的错误。这是单元测试还不够的地方。 ;)
  • 好的,我会以接收字节数无关紧要的方式来实现它。只是为了确保我理解正确。传输的字节不会以任何方式得到解释。这只是一个巧合,可能是由于发件人在各个消息之间的任何延迟造成的。
猜你喜欢
  • 2014-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多