TCP `net.Conn.Read` 在使用 `encoding/gob` 解码器后挂起
TCP `net.Conn.Read` hangs following use of an `encoding/gob` decoder
我可以用 encoding/gob
en/decoder 和 en/decode 成功地包裹 TCP net.Conn
的末端,但是如果我遵循 Decode
在原始连接上使用 Read
它挂在 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
或者如果我在 Decode
后面使用 Write
则不会发生这种情况(即交换 Read
/ Write
继 en/decode)。当我删除 en/decode 代码时,en/decode 之后的代码也可以独立工作。
是什么导致了这个挂起?这感觉像是一个缓存问题,但我不知道为什么 Write
不会刷新或者为什么 Read
不会提取最新的可用数据,或者为什么这个问题只出现在 gob
en/decoding 涉及。
gob
将 reader 包装在 bufio.Reader
中,如果 reader 还不是 bufio,您实际上有 2 个选项:
- 将你的 conn 包裹在 bufio.Reader 中并将其传递给 gob 并从那时起使用它。
- 一切都使用 gob,不要手动 read/write。
我可以用 encoding/gob
en/decoder 和 en/decode 成功地包裹 TCP net.Conn
的末端,但是如果我遵循 Decode
在原始连接上使用 Read
它挂在 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
或者如果我在 Decode
后面使用 Write
则不会发生这种情况(即交换 Read
/ Write
继 en/decode)。当我删除 en/decode 代码时,en/decode 之后的代码也可以独立工作。
是什么导致了这个挂起?这感觉像是一个缓存问题,但我不知道为什么 Write
不会刷新或者为什么 Read
不会提取最新的可用数据,或者为什么这个问题只出现在 gob
en/decoding 涉及。
gob
将 reader 包装在 bufio.Reader
中,如果 reader 还不是 bufio,您实际上有 2 个选项:
- 将你的 conn 包裹在 bufio.Reader 中并将其传递给 gob 并从那时起使用它。
- 一切都使用 gob,不要手动 read/write。