0

所以我会先说我的多线程程序无法输出 gzip 可以成功解压缩的东西,所以可能还有其他问题。但是我注意到的是,单线程和多线程的每个块的压缩大小是完全不同的。

在我的单线程运行中,我有一个设置了 SYNC_FLUSH 的 GZIPOutputStream(System.out, true)。我不断地从 system.in 读取,直到我的缓冲区已满。

GZIPOutputStream compressor = new GZIPOutputStream(System.out, true);
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);
....
while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        compressor.write(buff,0,offset);
        compressor.flush();
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        compressor.write(buff,0,offset);
        compressor.finish();
    }
}
compressor.close();

如您所见,在缓冲区满了之后,我告诉压缩器写入输出,然后调用flush。确保我强制它压缩并清除任何剩余的输出,因此当它再次写入时,缓冲区中没有任何剩余数据。

因此,它与您的原始输入开始时的长度非常相似(因此每个块都是它自己的单独流)。

所以在我的多线程程序中,我没有一个 GZIPOutputStream 写入和刷新,而是有一堆线程,每个线程都有自己的 GZIPOutputStream。所以基本上,用对线程的调用替换该部分

List<Future<byte[]>> results = new ArrayList<Future<byte[]>>();
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);

while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        results.add(exec.submit(new workerThread(buff,offset)));
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        results.add(exec.submit(new workerThread(buff,offset)));
    }
}

我只是将缓冲区传递给他们进行压缩。我所有的线程都是

private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
private byte[] finalOut;
....
public byte[] call() {
    try{
        GZIPOutputStream compress = new GZIPOutputStream (bOut, true);
        compress.write(input,0,size);
        compress.flush();
        compress.close();
     }
     catch (IOException e)
     {
        e.printStackTrace();
        System.exit(-1);
     }
     finalOut = bOut.toByteArray();
     return finalOut;
 }

我认为我所做的只是将压缩工作交给线程。我没有改变其他任何东西。但是,当我运行我的多线程程序并 hexdump 结果时,我注意到两个程序之间的每个块通常有很大不同。我使用了一个小缓冲区和小输入,因此更容易阅读。

我的多线程程序出现 crc 错误,这意味着至少 gzip 可以识别格式并开始解压缩。只是当它完成时,最终的结果与它期望的 CRC 不匹配(例如解压缩输出的大小等)。

老实说,我不确定为什么会发生这种情况。我本来预计会出现一些更明显的错误,但这个错误似乎很随机。肯定是压缩的。并且单线程和多线程程序之间的前几个字节(当然是在标头之后)通常是相同的,所以我认为我没有乱序连接(加上 executor.get() 函数应该处理) .

我只是难过。我知道 gzip 可以解压缩连接的流。我实际上将我的输入分成两半并分别输出,然后将它们组合到我的单线程程序中,它解压缩得很好。

作为记录,我只是在一个包含 328 个“A”字符的文件上尝试过,所以它不是很大。单个线程的 GZIPOutputStream 的 hexdump 是

0000000 8b1f 0008 0000 0000 0000 7472 581c 0000
0000010 0000 ffff 681a 0004 0000 ffff 21a2 02e2
0000020 0000 ff00 03ff a800 5bff 5c79 0001 0000

对于多线程来说

0000000 8b1f 0008 0000 0000 0000 7472 19a4 22e0
0000010 1146 0000 ff00 03ff 7500 5f6c 80d1 0000
0000020 1f00 088b 0000 0000 0000 a200 e221 4622
0000030 0011 0000 ffff 0003 6c75 d15f 0080 0000
0000040 8b1f 0008 0000 0000 0000 21a2 02e2 0000
0000050 ff00 03ff 8a00 193b 5c21 0000 0000     

他们很不一样。

哇,这最终真的很长。对于那个很抱歉。只是真的很困惑和卡住。

4

2 回答 2

1

不需要 flush()and调用。将完成,并且调用只是将不必要的空块添加到 deflate 流中。因为你不需要,你不需要设置true,因为它不会做任何事情。finish()close()flush()flush()syncFlush

制作一个大的 gzip 流和制作一堆小的 gzip 流当然会产生完全不同的结果。每个 gzip 流的头部和尾部都有 18 个字节的开销。对于您正在使用的小块,该开销完全支配了结果。

您的线程示例中有一个很大的错误。虽然无线程示例压缩了 328 个“A”,但线程示例将“A”和换行符(十进制 10)混合在一起。也许您应该开始甚至不尝试压缩,看看您是否可以分解一些输入(真实文本,而不仅仅是一系列相同的字符),将块发送到线程,让线程对数据完全不做任何事情,并且然后正确地重建原始输入。一旦你能做到这一点,然后回来。

于 2012-10-31T06:06:23.840 回答
0

从某处开始:

他们很不一样。

如果我从评论中的假设成立 (gzip 满足 unzip(gzip(a+b))=unzip(gzip(a) + gzip(b)) 对于两个字符串 a,b),那么这是预期的行为。

根据RFC,每个 gzip 调用都会写入一个标头。在Java中:

private void writeHeader() throws IOException {
    out.write(new byte[] {
                  (byte) GZIP_MAGIC,        // Magic number (short)
                  (byte)(GZIP_MAGIC >> 8),  // Magic number (short)
                  Deflater.DEFLATED,        // Compression method (CM)
                  0,                        // Flags (FLG)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Extra flags (XFLG)
                  0                         // Operating system (OS)
              });
}

GZIP_MAGIC 是 8b1f:

private final static int GZIP_MAGIC = 0x8b1f;

Deflater.DEFLATED 为 8:

public static final int DEFLATED = 8;

标题将以:
1f 8b 80 ...
您可以在输出中清楚地看到这部分(字节交换)。对于每个新的 gzip 部分,标题都会重新开始。因此,您的分块输出的长度必须大于正常输出。

关于多线程问题:我需要一个完整的示例来查看发生了什么。

于 2012-10-31T01:25:34.483 回答