使用Pipe可以消除TCP碎片吗?

时间:2013-12-11 22:05:07

标签: java tcp buffer pipe bytebuffer

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!")
}

但是这会保证完全填满缓冲区吗?或者管道是否会再次引入碎片?换句话说,是否会达到病情的身体?

2 个答案:

答案 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的附件,以便在需要时找到它。

我也不明白这一点:

  

如果可以在运行中重新组合多个频道,则解析会更容易

当然你的意思是多个缓冲区,但基本的想法是首先只有一个缓冲区。