Golang中compress/flate包可能存在bug?
Golang中compress/flate包可能存在bug? 使用 compress/flate 压缩数据包流时,解压缩器在 32767 (2^15) 次迭代后失败,并报告遇到意外的 EOF。
以下是演示该问题的示例代码:
package main
import (
"bytes"
"compress/flate"
"fmt"
"io"
)
func main() {
var buf bytes.Buffer
w, _ := flate.NewWriter(&buf, flate.DefaultCompression)
w.Write([]byte("hello, world\n"))
w.Close()
r := flate.NewReader(&buf)
defer r.Close()
for i := 0; i < 32768; i++ {
var out bytes.Buffer
_, err := io.Copy(&out, r)
if err != nil {
fmt.Printf("Iteration %d: %v\n", i, err)
break
}
fmt.Printf("Iteration %d: %s", i, out.String())
}
}
更多关于Golang中compress/flate包可能存在bug?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我不认为在解压缩器下重置读取器是受支持的操作。解压缩器拥有自己的状态。有一个方法可以将解压缩器重置为使用新的底层读取器,您应该改用这个方法。
// 代码示例应放置在此处
更多关于Golang中compress/flate包可能存在bug?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
它无法知道这一点,但它能注意到你的操作不符合预期。32768这个数字并非随机。也许flate数据流预期在该间隔处有额外的块头或其他内容(我对此并不确定,但这不难想象),而你无意中将其放入了下一条"消息"。将单个数据流分割成多条消息并在其间插入EOF显然不是正确的使用方式。
如果你需要独立的消息,压缩器和解压器都应当针对每条消息进行重置。
您提到可能有多余数据被添加到流中,这让我思考在解压缩步骤后是否会有残留数据。果然存在这种情况,只需要在下一条消息之前将其重新输入,实际上就形成了一个滑动字节缓冲区。
https://play.golang.org/p/TysU48fSMZQ
我希望避免重置压缩器/解压缩器,因为那样会降低压缩率,特别是对于较小的消息。而且compress/flate看起来就是为支持这种情况而设计的。
感谢您的查看。
这引出了你最初关于使用重置功能的评论。
我将 bytes.Reader 切换为 bytes.Buffer,并使用 Write 方法直接追加新数据 https://play.golang.org/p/gzzmc-2KA5A
我最初对使用 Write 追加数据感到担忧,因为我不想将整个网络流都保留在内存中,但查看 bytes.Buffer.Read 的实现,发现如果缓冲区为空,它会在内部调用 Reset 方法。https://github.com/golang/go/blob/master/src/bytes/buffer.go#L297
正如 Go 语言中常见的情况,简单的解决方案往往就是正确的解决方案。
这是一个已知的行为,而不是 compress/flate 包的 bug。问题在于 flate.NewReader 创建的读取器在遇到 EOF 后无法自动重置,导致后续读取失败。
在您的代码中,第一次调用 io.Copy 后,读取器 r 已经到达数据流的末尾。后续迭代尝试从已耗尽的读取器读取数据,因此返回 EOF 错误。
以下是正确的实现方式,每次迭代都创建新的读取器:
package main
import (
"bytes"
"compress/flate"
"fmt"
"io"
)
func main() {
var buf bytes.Buffer
w, _ := flate.NewWriter(&buf, flate.DefaultCompression)
w.Write([]byte("hello, world\n"))
w.Close()
compressedData := buf.Bytes()
for i := 0; i < 32768; i++ {
r := flate.NewReader(bytes.NewReader(compressedData))
var out bytes.Buffer
_, err := io.Copy(&out, r)
r.Close()
if err != nil {
fmt.Printf("Iteration %d: %v\n", i, err)
break
}
fmt.Printf("Iteration %d: %s", i, out.String())
}
}
如果您需要重复使用同一个读取器,可以使用 Reset 方法:
package main
import (
"bytes"
"compress/flate"
"fmt"
"io"
)
func main() {
var buf bytes.Buffer
w, _ := flate.NewWriter(&buf, flate.DefaultCompression)
w.Write([]byte("hello, world\n"))
w.Close()
r := flate.NewReader(&buf)
defer r.Close()
compressedData := buf.Bytes()
for i := 0; i < 32768; i++ {
var out bytes.Buffer
// 重置读取器以重新开始解压缩
r.(flate.Resetter).Reset(&buf, nil)
_, err := io.Copy(&out, r)
if err != nil {
fmt.Printf("Iteration %d: %v\n", i, err)
break
}
fmt.Printf("Iteration %d: %s", i, out.String())
}
}
关键点在于理解 flate.Reader 在到达 EOF 后不会自动回滚,需要显式重置或重新创建才能再次读取相同的数据。


