問題是什么?
我無法使用 go 的zlib包從 zlib 流中解碼有效的壓縮塊。
我準備了一個 github 存盤庫,其中包含說明我遇到的問題的代碼和資料:https : //github.com/andreyst/zlib-issue。
那些塊是什么?
它們是由文本游戲服務器 (MUD) 生成的訊息。該游戲服務器以多個塊發送壓縮訊息流,其中第一個包含 zlib 標頭,其他則不包含。
我使用名為“mcclient”的代理捕獲了兩個塊(第一個和第二個),這是一個 sidecar,為不支持壓縮的 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然后嘗試解壓縮塊0并1逐步進行。
嘗試 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 的包?也許我使用它不正確?
如果不是,解決方法是什么?也很有趣的是,為什么會這樣——是設計使然嗎?只是還沒有實施嗎?我錯過了什么?
uj5u.com熱心網友回復:
請注意,錯誤表示輸入后偏移處的資料已損壞。那是因為您從檔案中讀取的方式:
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缺少校驗和資料,但我認為這只是一個意外。
uj5u.com熱心網友回復:
通過仔細除錯,我能夠看到我錯誤地傳遞了太大的緩沖區切片,這導致了不正確的輸入緩沖區被提供給解壓縮。
此外,重要的是不要使用io.Copy,這會導致緩沖區上的 EOF 停止所有內容,而是僅使用 zlibReader.Read(),它將解壓縮當前緩沖區中的所有內容。
我已經更新了代碼,現在它可以按預期作業了。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/400776.html
