10

我正在使用 Kaitai-Struct 在 Java 中解析大型 PCAP 文件。每当文件大小超过Integer.MAX_VALUE字节时,我都会面临IllegalArgumentException由底层ByteBuffer.

我没有在其他地方找到对这个问题的引用,这让我相信这不是图书馆的限制,而是我使用它的方式的错误。

由于问题是由于尝试将整个文件映射到文件中引起的,因此ByteBuffer我认为解决方案将仅映射文件的第一个区域,并且随着数据的消耗,映射再次跳过已解析的数据。

由于这是在 Kaitai Struct Runtime 库中完成的,这意味着编写我自己的扩展 fom 的类KatiaiStream并覆盖自动生成的fromFile(...)方法,这似乎不是正确的方法。

从文件中解析 PCAP 类的自动生成方法是。

public static Pcap fromFile(String fileName) throws IOException {
  return new Pcap(new ByteBufferKaitaiStream(fileName));
}

Kaitai ByteBufferKaitaiStreamStruct Runtime 库提供的ByteBuffer.

private final FileChannel fc;
private final ByteBuffer bb;

public ByteBufferKaitaiStream(String fileName) throws IOException {
    fc = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
    bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
}

这反过来又受到ByteBuffer最大尺寸的限制。

我错过了一些明显的解决方法吗?这真的是对Java中Katiati Struct实现的限制吗?

4

2 回答 2

2

这里有两个单独的问题:

  1. 运行Pcap.fromFile()大文件通常不是一种非常有效的方法,因为您最终会将所有文件一次解析到内存数组中。kaitai_struct/issues/255中给出了如何避免这种情况的示例。基本思想是,您希望控制读取每个数据包的方式,然后在以某种方式解析/计算每个数据包后处理每个数据包。

  2. Java 的映射文件限制为 2GB。为了缓解这种情况,您可以使用替代的基于 RandomAccessFile 的 KaitaiStream 实现:RandomAccessFileKaitaiStream——它可能会更慢,但它应该避免 2GB 的问题。

于 2019-05-20T19:50:26.060 回答
1

long这个库提供了一个使用偏移量的 ByteBuffer 实现。我没有尝试过这种方法,但看起来很有希望。请参阅大于 2 GB 的映射文件部分

http://www.kdgregory.com/index.php?page=java.byteBuffer

public int getInt(long index)
{
    return buffer(index).getInt();
}

private ByteBuffer buffer(long index)
{
    ByteBuffer buf = _buffers[(int)(index / _segmentSize)];
    buf.position((int)(index % _segmentSize));
    return buf;
}
public MappedFileBuffer(File file, int segmentSize, boolean readWrite)
throws IOException
{
    if (segmentSize > MAX_SEGMENT_SIZE)
        throw new IllegalArgumentException(
                "segment size too large (max " + MAX_SEGMENT_SIZE + "): " + segmentSize);

    _segmentSize = segmentSize;
    _fileSize = file.length();

    RandomAccessFile mappedFile = null;
    try
    {
        String mode = readWrite ? "rw" : "r";
        MapMode mapMode = readWrite ? MapMode.READ_WRITE : MapMode.READ_ONLY;

        mappedFile = new RandomAccessFile(file, mode);
        FileChannel channel = mappedFile.getChannel();

        _buffers = new MappedByteBuffer[(int)(_fileSize / segmentSize) + 1];
        int bufIdx = 0;
        for (long offset = 0 ; offset < _fileSize ; offset += segmentSize)
        {
            long remainingFileSize = _fileSize - offset;
            long thisSegmentSize = Math.min(2L * segmentSize, remainingFileSize);
            _buffers[bufIdx++] = channel.map(mapMode, offset, thisSegmentSize);
        }
    }
    finally
    {
        // close quietly
        if (mappedFile != null)
        {
            try
            {
                mappedFile.close();
            }
            catch (IOException ignored) { /* */ }
        }
    }
}
于 2019-05-20T09:45:09.353 回答