TCP`net.Conn.Read`在使用`encoding / gob`解码器后挂起

时间:2016-05-19 17:49:37

标签: go tcp network-programming deadlock gob

我可以使用net.Conn en / decoder包裹TCP encoding/gob的末尾,并通过它成功地对其进行en / decode,但是如果我跟Decode一起使用{{1}它挂在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")
}

这会导致Go Playground中出现deadlock panic并挂起我的本地计算机(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 version go1.6.2 darwin/amd64或者我跟net.PipeConn使用Decode,则不会发生这种情况(即交换Write / {{的顺序1}}跟随en / decode)。当我删除en / decode代码时,en / decode之后的代码也可以单独工作。

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

1 个答案:

答案 0 :(得分:1)

gob将读者包裹在bufio.Reader中,如果读者已经不是bufio,那么你真的有两个选择:

  1. 将你的conn包裹在bufio.Reader中并将其传递给gob并从那时起使用它。
  2. 将gob用于所有内容,不要手动读/写。