【发布时间】:2017-07-23 16:47:31
【问题描述】:
我正在编写一个与普通 Java 客户端配对的玩具 Java NIO 服务器。客户端使用普通 Socket 向服务器发送字符串消息。服务器接收到消息并将内容转储到终端。
我注意到来自客户端的相同消息每次都以不同的方式分解为字节缓冲区。我了解这是 NIO 的预期行为,但想大致了解 NIO 是如何决定截断消息的?
示例:向服务器发送字符串“这是一条测试消息”。以下是服务器日志的摘录(每行代表接收到的 1 个字节缓冲区)。
Run 1:
Server receiving: this is a test message
Run 2:
Server receiving: t
Server receiving: his is a test message
Run 3:
Server receiving: this is
Server receiving: a test message
更新 - 问题已解决
我已经安装了 Wireshark 来分析数据包,很明显,随机“分手”是由于我使用 DataOutputStream 作为作者,它逐个字符地发送消息!所以每个角色都有一个数据包...
将作者更改为BufferedWriter 后,我的短消息现在按预期作为单个数据包发送。所以事实是,Java NIO 实际上做了一件聪明的事,并将我的小数据包合并到 1 到 2 个字节缓冲区!
UPDATE2 - 澄清
感谢大家的回复。感谢@StephenC 指出,除非我自己对消息进行编码(是的,我在写信给BufferedWriter 后确实调用了flush()),否则我的消息总是有可能通过多个数据包到达。
所以事实是,Java NIO 实际上做了一件聪明的事,并合并了我的 tiny
实际上,没有。合并发生在 BufferedWriter 层中。当应用程序刷新或关闭 DataOutputStream 或 BufferdWriters 缓冲区填满时,缓冲写入器只会将“一堆”字节传递给 NIO 层。
我实际上指的是我第一次尝试使用DataOutputStream(我从网上的一个例子中得到它,既然你已经指出了,这显然是对类的不正确使用)。 BufferedWriter 没有参与。在那种情况下,我的简单作家就像
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeBytes("this is a test message");
Wireshark 确认此消息已发送(本地主机上的服务器)1 个字符一个数据包(实际消息总共 22 个数据包,不包括所有 ACK 等)。
我可能错了,但这种行为似乎表明 NIO 服务器将这 22 个数据包组合成 1-2 个字节缓冲区?
我在这里尝试实现的最终游戏是一个简单的 Java NIO 服务器,能够使用 TCP 从各种客户端接收请求和数据流,其中一些可能由第三方用 C++ 或 C# 编写。这不是时间关键,因此客户端可以一次性发送所有数据,服务器可以按照自己的节奏处理它们。这就是为什么我使用普通的Socket 而不是 NIO 客户端在 Java 中编写了一个玩具客户端。因此,这种情况下的客户端不能真正直接操作ByteBuffer,所以我可能需要某种消息格式。我可以完成这项工作吗?
【问题讨论】:
-
欢迎来到 SO。通常,最好发布您所指的相关代码,而不是试图通过文字来解释该代码。
-
@m0skit0 感谢您的建议。这更像是一个关于 Java NIO 如何工作的一般问题。我将在原始帖子中添加一个示例。 :-)
-
这不是“NIO 的预期行为”,而是 TCP 的实际行为。 NIO 没有做任何事情来改变这一点。合并数据包的是
BufferedWriter,而不是 NIO。