TCP网络消息可以分段。但是碎片消息很难解析,特别是当传输长于一个字节的数据类型时。例如buffer.getLong()
可能会失败,如果我期望long
的某些字节在第二个缓冲区中结束。
如果可以在运行中重新组合多个频道,则解析会更容易。所以我想通过java.nio.channels.Pipe
发送所有数据。
// count total length
int length = 0;
foreach (Buffer buffer: buffers) {
length += buffer.remaining()
}
// write to pipe
Pipe pipe = Pipe.open();
pipe.sink().write(buffers);
// read back from pipe
ByteBuffer complete = ByteBuffer.allocateDirect(length)
if (pipe.source().read(complete) != length) {
System.out.println("Fragmented!")
}
但是这会保证完全填满缓冲区吗?或者管道是否会再次引入碎片?换句话说,是否会达到病情的身体?
答案 0 :(得分:1)
TCP碎片与您遇到的问题几乎没有关系。流的源栈上的TCP堆栈将对于单个数据包而言太大的消息划分为多个数据包,并且它们到达并重新组装可能不符合您期望的长数。
无论如何,您将对作为输入流的字节数组(ByteBuffer
)进行处理。您告诉JVM将“缓冲区中剩余的内容”读入ByteBuffer
。同时,long
的后半部分现在位于网络缓冲区内。您现在正在尝试阅读的ByteBuffer
将永远不会有long
的剩余部分。
考虑使用Scanner
来读取长片,它会阻塞直到可以读取长片。
Scanner scanner= new Scanner(socket.getChannel());
scanner.nextLong();
另外考虑使用DataInputStream
来读取long,尽管我无法判断它是否会阻塞,直到根据文档读取整个long
。
DataInputStream dis = new DataInputStream(socket.InputStream);
dis.readLong();
如果您可以控制服务器,请考虑使用flush()
来阻止您的数据包被缓冲并发送“碎片”或ObjectOutputStream
/ ObjectInputStream
作为更方便的方式IO
答案 1 :(得分:0)
没有。 Pipe
旨在由一个线程写入并由另一个线程读取。有一个只有4k的内部缓冲区。如果你写的不止于此,你就会失速。
除了演示之外,它们实际上并没有多大用处。
我不明白这一点:
例如,如果long的某些字节在第二个缓冲区中结束,则buffer.getLong()可能会失败。
什么第二个缓冲区?您应该在通道的生命周期中使用相同的接收缓冲区。将其作为SelectionKey
的附件,以便在需要时找到它。
我也不明白这一点:
如果可以在运行中重新组合多个频道,则解析会更容易
当然你的意思是多个缓冲区,但基本的想法是首先只有一个缓冲区。