【问题标题】:TCP `net.Conn.Read` hangs following use of an `encoding/gob` decoderTCP `net.Conn.Read` 在使用`encoding/gob` 解码器后挂起
【发布时间】:2016-09-16 18:28:59
【问题描述】:

我可以用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 中出现 deadlock panic 并挂在我的本地计算机 (go version go1.6.2 darwin/amd64) 上,尽管代码会间歇性地在本地执行完成。

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

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

【问题讨论】:

    标签: go tcp network-programming deadlock gob


    【解决方案1】:

    gob 将阅读器包装在 bufio.Reader 中,如果阅读器还不是 bufio,那么您确实有 2 个选择:

    1. 将您的 conn 包装在 bufio.Reader 中并将其传递给 gob 并从那时起使用它。
    2. 一切都使用 gob,不要手动读/写。

    【讨论】:

    • 啊,我想我明白了,在较低级别的术语中,缓冲区可能会读取我发送的额外字节?非常感谢您的回答!
    猜你喜欢
    • 2017-05-01
    • 1970-01-01
    • 2012-12-16
    • 2021-06-14
    • 2016-01-09
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 2019-08-07
    相关资源
    最近更新 更多