为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Go 中解码 zlib 流?

如何在 Go 中解码 zlib 流?

Go
慕容森 2022-10-24 09:39:32
问题是什么?我无法使用 go 的zlib包从 zlib 流中解码有效的压缩块。我准备了一个 github 存储库,其中包含说明我遇到的问题的代码和数据:https ://github.com/andreyst/zlib-issue 。那些块是什么?它们是由文本游戏服务器 (MUD) 生成的消息。该游戏服务器以多个块发送压缩的消息流,其中第一个包含 zlib 标头,其他不包含。我使用名为“mcclient”的代理捕获了两个块(第一个和第二个),它是一个边车,为不支持压缩的 MUD 客户端提供压缩。它是用 C 语言编写的,并使用 Czlib库来解码压缩块。块包含在“块”目录中,并以数字0和1. *.in文件包含压缩数据。*.out包含从 mcclient 捕获的未压缩数据。*.log包含 zlib 解压的状态(inflate调用的返回码)。一个特殊的all.in块是与块0连接的块1。为什么我认为它们是有效的?mcclient使用 C 成功解压缩输入块,zlib没有任何问题。*.logstatus 显示0这意味着 Z_OK 这意味着 zlib 用语中没有错误。zlib-flate -uncompress < chunks/all.in在 Linux 下可以正常工作并解压缩为相同的内容。在 Mac OS 下,它也解压缩到相同的内容,但带有警告zlib-flate: WARNING: zlib code -5, msg = input stream is complete but output may still be valid——这看起来与预期的一样,因为块不包含“官方”流结束。中的 Python 代码decompress.py可以正确解压缩all.in和0/1块,没有任何问题。go的zlib有什么问题?看main.go——它尝试解压缩这些块,从开始然后all.in尝试逐步解压缩块。01尝试 decode all.in( func all()) 有点成功,至少解压后的数据是一样的,但是 zlib reader 返回 error flate: corrupt input before offset 446。在尝试逐块解压缩块的实际场景(func stream())时,zlib 读取器使用预期数据解码第一个块,但返回错误flate: corrupt input before offset 32,随后尝试解码块1完全失败。问题zlib是否可以在适合这种场景的某种“流”模式下使用 go 的包?也许我使用不正确?如果没有,解决方法是什么?同样有趣的是,为什么会这样——这是设计使然吗?只是还没有实施吗?我错过了什么?
查看完整描述

2 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

请注意,错误表示输入后偏移处的数据已损坏。那是因为您从文件中读取的方式:


    buf := make([]byte, 100000)

    n, readErr := f.Read(buf)

    if readErr != nil {

        log.Fatalf("readErr=%v\n", readErr)

    }

    fmt.Printf("Read bytes, n=%v\n", n)


    buffer := bytes.NewBuffer(buf)

    zlibReader, zlibErr := zlib.NewReader(buffer)

    if zlibErr != nil {

        log.Fatalf("zlibErr=%v\n", zlibErr)

    }

buf := make([]byte, 100000)将制作一个 100000 字节的切片,全部为 0。但在all.in. 由于您从不缩短切片,因此阅读器将在有效数据之后遇到几千个零并断定它已损坏。这就是你得到输出和错误的原因。


至于流媒体。在 TCP/UDP 连接的情况下,您应该能够将 a 连接传递io.Reader给zlib.NewReader. 为了模拟相同的内容,我在修改后的代码中使用了io.Pipe:


package main


import (

    "bytes"

    "compress/zlib"

    "fmt"

    "io"

    "log"

    "os"


    otherzlib "github.com/4kills/go-zlib"

)


func main() {

    all()

    stream()


    // Alas it hangs :(

    // otherZlib()

}


func all() {

    fmt.Println("==== RUNNING DECOMPRESSION OF all.in")

    fmt.Println("")


    buf, readErr := os.ReadFile("./chunks/all.in")

    if readErr != nil {

        log.Fatalf("readErr=%v\n", readErr)

    }

    fmt.Printf("Read bytes, n=%v\n", len(buf))


    buffer := bytes.NewBuffer(buf)

    zlibReader, zlibErr := zlib.NewReader(buffer)

    if zlibErr != nil {

        log.Fatalf("zlibErr=%v\n", zlibErr)

    }


    out := new(bytes.Buffer)

    written, copyErr := io.Copy(out, zlibReader)

    if copyErr != nil {

        log.Printf("copyErr=%v\n", copyErr)

    }

    fmt.Printf("Written bytes, n=%v, out:\n%v\n", written, out.String())

    fmt.Println("")

}


func stream() {

    fmt.Println("==== RUNNING DECOMPRESSION OF SEPARATE CHUNKS")

    fmt.Println("")


    pRead, pWrite := io.Pipe()

    go func() {

        buf, readErr := os.ReadFile("./chunks/0.in")

        if readErr != nil {

            log.Fatalf("readErr=%v\n", readErr)

        }

        fmt.Printf("Read 0 bytes, n=%v\n", len(buf))


        written0, copy0Err := io.Copy(pWrite, bytes.NewBuffer(buf))

        if copy0Err != nil {

            log.Printf("copy0Err=%v\n", copy0Err)

        }

        fmt.Printf("Written compressed bytes, n0=%v", written0)


        buf, readErr = os.ReadFile("./chunks/1.in")

        if readErr != nil {

            log.Fatalf("read1Err=%v\n", readErr)

        }

        fmt.Printf("Read 1 bytes, n=%v\n", len(buf))


        written1, copy1Err := io.Copy(pWrite, bytes.NewBuffer(buf))

        if copy1Err != nil {

            log.Printf("copy1Err=%v\n", copy1Err)

        }

        fmt.Printf("Written compressed bytes, n1=%v", written1)


        pWrite.Close()

    }()


    zlibReader, zlibErr := zlib.NewReader(pRead)

    if zlibErr != nil {

        log.Fatalf("zlibErr=%v\n", zlibErr)

    }


    out := new(bytes.Buffer)

    written2, copy2Err := io.Copy(out, zlibReader)

    if copy2Err != nil {

        log.Printf("copy2Err=%v\n", copy2Err)

    }

    fmt.Printf("Written decompressed bytes, n0=%v, out:\n%v\n", written2, out.String())


    fmt.Println("")

}

使用此代码,我没有收到任何错误,stream()但我仍然收到copyErr=unexpected EOF错误all(),看起来最后all.in缺少校验和数据,但我认为这只是一个意外。


查看完整回答
反对 回复 2022-10-24
?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

通过仔细调试,我能够看到我错误地传递了太大的缓冲区切片,这导致不正确的输入缓冲区被馈送到解压缩。

此外,重要的是不要使用io.Copy,这会导致缓冲区上的 EOF 停止一切,而是只使用 zlibReader.Read(),这将解压缩当前在缓冲区中的所有内容。

我已经更新了代码,所以它现在可以按预期工作。


查看完整回答
反对 回复 2022-10-24
  • 2 回答
  • 0 关注
  • 312 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号