可以使用管道消除TCP碎片吗



TCP网络消息可以被分段。但是碎片消息很难解析,尤其是当传输的数据类型超过一个字节时。例如,如果我期望的long的一些字节最终在第二缓冲器中,则buffer.getLong()可能失败。

如果多个通道可以在运行中重新组合,那么解析会容易得多。所以我想通过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!")
}

但这能保证完全填满缓冲区吗?还是管道会再次引入碎片化?换言之,这种状况会达到吗?

TCP碎片与您遇到的问题几乎没有关系。流源上的TCP堆栈正在将对于单个数据包来说太大的消息划分为多个数据包,这些消息正在到达并重新组装,可能与您期望的长度不一致。

无论如何,您将相当于字节数组(ByteBuffer)的内容视为输入流。您告诉JVM将"缓冲区中的其余部分"读取到ByteBuffer中。同时,您的long的后半部分现在在网络缓冲区内。您现在试图通读的ByteBuffer将永远不会拥有该long的其余部分。

考虑使用Scanner读取long,它将阻塞直到可以读取long。

Scanner scanner= new Scanner(socket.getChannel());
scanner.nextLong();

也可以考虑使用DataInputStream来读取long,尽管在根据文档读取整个long之前,我无法判断它是否会阻塞。

DataInputStream dis = new DataInputStream(socket.InputStream);
dis.readLong();

如果您可以控制服务器,可以考虑使用flush()来防止数据包被缓冲并"碎片化"发送,或者使用ObjectOutputStream/ObjectInputStream作为更方便的IO方式。

否。Pipe旨在由一个线程写入,并由另一个线程读取。内部缓冲区只有4k。如果你给它写的东西不止这些,你就会拖延。

实际上,除了作为一个示范,它们根本没有多大用处。

我不明白:

例如,buffer.getLong()可能会失败,如果我期望的长度中的一些字节最终在第二个缓冲区中。

什么第二缓冲区?在通道的使用寿命内,您应该使用相同的接收缓冲区。把它作为SelectionKey的附件,这样你就可以在需要的时候找到它。

我也不明白:

如果多个通道可以在飞行中重新组合,解析将更容易

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

最新更新