How does Java NIO break up messages?

I'm writing a toy Java NIO server paired with a normal Java client. The client sends a string message to the server using plain Socket. The server receives the message and dumps the content to terminal.

I've noticed that the same message from client is broken up into bytebuffers differently every single time. I understand this is intended behaviour of NIO, but would like to find out roughly how the NIO decides to chop up a message?

Example: Sending string "this is a test message" to server. The following are excerpts of server loggings (each line represents 1 bytebuffer received).

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

UPDATE - Issue Resolved

I have installed Wireshark to analyse the packets and it has become apparent that the random "break up" was due to me using DataOutputStream for the writer, which sends the message character by character! So there was a packet for each character...

After changing the writer to BufferedWriter , my short message is now sent as a single packet, as expected. So the truth is Java NIO actually did the clever thing and merged my tiny packets to 1 to 2 bytebuffers!


UPDATE2 - Clarification

Thank you all for your replies. Thank you @StephenC for pointing out that unless I encode the message myself(yes, I did call flush() after writing to BufferedWriter ), there's always the possiblity of my message arriving across multiple packets.

So the truth is Java NIO actually did the clever thing and merged my tiny

Actually, no. The merging is happening in the BufferedWriter layer. The buffered writer will only deliver a "bunch" of bytes to the NIO layer when either the application flushes or closes the DataOutputStream or the BufferdWriters buffer fills up.

I was in fact referring to my first attempt with DataOutputStream (I got it from an example online, which obviously is incorrect use of the class now that you've pointed it out). BufferedWriter was not involved. My simple writer in that case went like

DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeBytes("this is a test message");

Wireshark confirmed that this message was sent(server on localhost) 1 character a packet(22 packets in total for the actual message not including all the ACK and etc).

I'm probably wrong, but this behaviour seems to suggest that the NIO server combined these 22 packets into 1-2 bytebuffers?


The end game I'm trying to achieve here is a simple Java NIO server capable of receiving request and data stream using TCP from various clients, some may be written in C++ or C# by third party. It's not time critical so the clients can send all data in one go and the server can process them at its own pace. That's why I've written a toy client in Java using plain Socket rather than a NIO client. Therefore the client in this case can't really manipulate the ByteBuffer directly, so I probably need some sort of message format. Could I make this work?


If you are sending data over a TCP/IP socket, then there are no "messages" as such. What you send and receive is a stream of bytes.

If you are asking if you can send a chunk of N bytes, and have the receiver get exactly N bytes in a single read call, then the answer is that there is no guarantee that will happen. However, it is the TCP/IP stack that is "breaking up" the "messages". Not NIO. Not Java.

Data sent over a TCP/IP connection is ultimately broken into network packets for transmission. This typically erases any "message" structure based on the original write request sizes.

If you want a reliable message structure over the top of the TCP/IP byte stream, you need to encode it in the stream itself; eg using an "end-of-message" marker or prefixing each message with a byte count. (If you want to use fancy words, you need to implement a "message protocol" over the top of the TCP/IP stream.)


Concerning your update, I think there are still some misconceptions:

... it became apparent that the random "break up" was due to me using DataOutputStream for the writer, which sends the message character by character! So there was a packet for each character...

Yes, lots of small writes to a socket stream may result in severe fragmentation at the network level. However, it won't always. If there is sufficient "back pressure" due to either network bandwidth constraints or the receiver reading slowly, then this will lead to larger packets.

After changing the writer to BufferedWriter, my short message is now sent as a single packet, as expected.

Yes. Adding buffering to the stack is good. However, you are probably doing something else; eg calling flush() after each message. If you didn't then I would expect a network packet to contain a sequence of messages and partial messages.

What is more, if the messages are too large to fit into a single network packet, or if there is severe back-pressure (see above) then you are liable to get multiple / partial messages in a packet anyway. Either way, the receiver should not rely on getting one (whole) message each time it reads.

In short, you may not have really resolved your issue!!

So the truth is Java NIO actually did the clever thing and merged my tiny

Actually, no. The merging is happening in the BufferedWriter layer. The buffered writer will only deliver a "bunch" of bytes to the NIO layer when either the application flushes or closes the DataOutputStream or the BufferdWriter s buffer fills up.


FWIW - given your description of what you are doing, it is unlikely using NIO is helping performance. If you wanted to maximize performance, you should stop using BufferedWriter and DataOutputStream . Instead do your message encoding "by hand", putting the bytes or characters directly into the ByteBuffer or CharBuffer .

(Also DataOutputStream is for binary data, not text. Putting one in front of a Writer doesn't seem right ... if that is what you are really doing.)

链接地址: http://www.djcxy.com/p/34142.html

上一篇: 如何在没有客户端的情况下直接使用nio发送消息

下一篇: Java NIO如何分解消息?