TCP“net.Conne.Read”在使用“encoding/gob”解码器后挂起



我可以用encoding/gob en/decoder包装TCP net.Conn的末端,并通过它成功地对值进行en/decode,但如果我在原始连接上用Read跟随Decode,它将挂在Read:上

package main
import (
    "encoding/gob"
    "net"
    "log"
    "sync"
)
func main() {
    var wg sync.WaitGroup
    addr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9000}
    ready := make(chan struct{})
    wg.Add(1)
    go func() {
        defer wg.Done()
        ln, err := net.ListenTCP("tcp4", addr)
        if err != nil {
            log.Fatal("ln: ", err)
        }
        defer ln.Close()
        close(ready)
        conn, err := ln.Accept()
        if err != nil {
            log.Fatal("conn: ", err)
        }
        defer conn.Close()
        var out string
        if err := gob.NewDecoder(conn).Decode(&out); err != nil {
            log.Fatal("error decoding: ", err)
        }
        if "hello" != out {
            log.Fatalf("1 expected '%s', got '%s'", "hello", out)
        }
        b := make([]byte, 1)
        log.Println("ready to read 1")
        if _, err := conn.Read(b); err != nil {
            log.Fatal("error reading: ", err)
        }
        log.Println("read 1")
        if b[0] != 1 {
            log.Fatalf("2 expected '%d', got '%d'", 1, b[0])
        }
        if _, err := conn.Write([]byte{1}); err != nil {
            log.Fatal("err writing2: ", err)
        }
        log.Println("done 1")
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        <-ready
        conn, err := net.DialTCP("tcp4", nil, addr)
        if err != nil {
            log.Fatal("conn2: ", err)
        }
        defer conn.Close()
        if err := gob.NewEncoder(conn).Encode("hello"); err != nil {
            log.Fatal("error encoding: ", err)
        }
        if _, err := conn.Write([]byte{1}); err != nil {
            log.Fatal("write error: ", err)
        }
        b := make([]byte, 1)
        log.Println("ready to read 2")
        if _, err := conn.Read(b); err != nil {
            log.Fatal("error reading2: ", err)
        }
        log.Println("read 2")
        if b[0] != 1 {
            log.Fatalf("3 expected '%d', got '%d'", 1, b[0])
        }
        log.Println("done 2")
    }()
    log.Println("waiting")
    wg.Wait()
    log.Println("waited")
}

输出:

2009/11/10 23:00:00 waiting
2009/11/10 23:00:00 ready to read 2
2009/11/10 23:00:00 ready to read 1

这会在Go Playground中导致死锁恐慌,并挂在我的本地机器(go version go1.6.2 darwin/amd64)上,尽管代码会间歇性地在本地执行到完成。

如果我使用net.PipeConn,或者如果我用Write代替Decode(即,在en/decode之后交换Read/Write的顺序),则不会发生这种情况。当我删除en/decode代码时,en/decode后面的代码也可以单独工作。

是什么导致了这种挂起?这感觉像是一个缓存问题,但我不知道为什么Write不会刷新,或者为什么Read不会提取最新的可用数据,或者为什么这个问题只在涉及gob en/解码时出现。

gob将读取器包装在bufio.Reader中。如果读取器还不是bufio,那么实际上有两个选项:

  1. 把你的conn包在bufio里。阅读并将其传递给gob,然后从那时起使用它
  2. 对所有内容都使用gob,不要手动读/写

最新更新