gzip Reader在golang 1.7.3中的行为变化(与1.6.3相比)

huangapple go评论95阅读模式
英文:

Behavior change of gzip Reader in golang 1.7.3 (compared to 1.6.3)

问题

我有一个程序,它从网络连接中读取gzip压缩的JSON数据包。发送方在发送gzip数据包后不关闭连接。在go1.6.3中,这个程序可以正常工作,也就是说,在接收到gzip结束序列后,程序可以解码gzip数据包。但是在go1.7.3中,程序会阻塞,因为没有收到io.EOF信号。

下面是一个使用管道模拟网络连接的示例(请注意,写入器保持打开状态以模拟开放的连接):

package main

import (
    "fmt"
    "encoding/json"
    "compress/gzip"
    "io"
    "runtime"
)

type TestJSON struct {
  TestString string `json:"test"`
}

func main() {
    fmt.Printf("Version: %s\n", runtime.Version())
    pipeReader, pipeWriter := io.Pipe();

    go writeTo(pipeWriter);
    readFrom(pipeReader);
}

func writeTo(pipeWriter *io.PipeWriter){
    // marshall and compress
    testJSON := TestJSON{TestString: "test",}

    jsonString, err := json.Marshal(testJSON)
    if err != nil {
      fmt.Printf("Marshalling Error: %s\n", err)
      return
    }

    gzipOut := gzip.NewWriter(pipeWriter)
    _, err = gzipOut.Write(jsonString)
    if err != nil {
      fmt.Printf("Error Writing: %s\n", err)
      return
    }
    gzipOut.Close()
    //pipeWriter.Close()
}

func readFrom(pipeReader *io.PipeReader){
    // decompress and unmarshall
    gzipIn, err := gzip.NewReader(pipeReader)
    if err != nil {
      fmt.Printf("Error creating reader: %s\n", err)
      return
    }
    defer gzipIn.Close()

    jsonDecoder := json.NewDecoder(gzipIn)
    msg := new(TestJSON)
    err = jsonDecoder.Decode(msg)
    if err != nil {
      fmt.Printf("Error decoding: %s\n", err)
      return
    }
    fmt.Printf("Received: %v\n", msg)
}

基于这种情况,我有两个问题:

  1. 哪种行为是正确的?
  2. 如果go1.7.3的行为是正确的,我如何在开放的网络连接上解码传入的gzip数据包?
英文:

I have a program that reads gzip compressed json packets from a network connection. The sender does not close the connection after sending the gzip packet. In go1.6.3 this works perfectly, i.e., the gzip packet is decoded after the gzip end-sequence is received, but in go1.7.3 the reader blocks as there is no io.EOF.

Here is a sample that simulates the network connection using a pipe (note that the writer stays open to simulate the open connection):

package main
import (
"fmt"
"encoding/json"
"compress/gzip"
"io"
"runtime"
)
type TestJSON struct {
TestString string `json:"test"`
}
func main() {
fmt.Printf("Version: %s\n", runtime.Version())
pipeReader, pipeWriter := io.Pipe();
go writeTo(pipeWriter);
readFrom(pipeReader);
}
func writeTo(pipeWriter *io.PipeWriter){
// marshall and compress
testJSON := TestJSON{TestString: "test",}
jsonString, err := json.Marshal(testJSON)
if err != nil {
fmt.Printf("Marshalling Error: %s\n", err)
return
}
gzipOut := gzip.NewWriter(pipeWriter)
_, err = gzipOut.Write(jsonString)
if err != nil {
fmt.Printf("Error Writing: %s\n", err)
return
}
gzipOut.Close()
//pipeWriter.Close()
}
func readFrom(pipeReader *io.PipeReader){
// decompress and unmarshall
gzipIn, err := gzip.NewReader(pipeReader)
if err != nil {
fmt.Printf("Error creating reader: %s\n", err)
return
}
defer gzipIn.Close()
jsonDecoder := json.NewDecoder(gzipIn)
msg := new(TestJSON)
err = jsonDecoder.Decode(msg)
if err != nil {
fmt.Printf("Error decoding: %s\n", err)
return
}
fmt.Printf("Recived: %v\n", msg)
}

Based on this situation I have 2 questions:

  1. Which is the correct behavior?
  2. If go1.7.3 behaves correctly, how can I decode incoming gzip packets on an open network connection?

答案1

得分: 2

你所看到的是正确的行为。gzip.Reader的旧行为是它能够在没有io.EOF的情况下返回最后的读取结果,然后在后续调用中返回(0, io.EOF)。现在它正确地等待io.EOF,它将阻塞等待下一个gzip头或io.EOF

如果你不希望有更多的文件,并且希望gzip尾部指示文件结束,而不管io.EOF如何,你需要设置Reader.Multistream(false)

在添加了这个设置之后,你的示例可以正常工作:

func readFrom(pipeReader *io.PipeReader) {
    // 解压缩和反序列化
    gzipIn, err := gzip.NewReader(pipeReader)
    if err != nil {
        fmt.Printf("创建读取器时出错:%s\n", err)
        return
    }
    gzipIn.Multistream(false)
}

https://play.golang.org/p/BdaulxMza0

英文:

What you're seeing is the correct behavior. The old behavior of the gzip.Reader was a side effect of it being able to return a final read without io.EOF, then (0, io.EOF) in a subsequent call. Now that it properly waits for io.EOF, it will block waiting for the next gzip header or io.EOF.

If you don't expect more files, and you want to have the gzip trailer indicate the end of the file regardless of io.EOF, you have to set Reader.Multistream(false).

Your example works with that addition:

func readFrom(pipeReader *io.PipeReader) {
// decompress and unmarshall
gzipIn, err := gzip.NewReader(pipeReader)
if err != nil {
fmt.Printf("Error creating reader: %s\n", err)
return
}
gzipIn.Multistream(false)

https://play.golang.org/p/BdaulxMza0

huangapple
  • 本文由 发表于 2016年11月18日 18:30:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/40674759.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定