dongzhang6544 2018-03-31 22:59
浏览 171
已采纳

服务器和客户端之间的明显死锁

I have a test function which both creates a server and spawns a goroutine acting as a client. Now, simply sending a message from the client to the server works, but if I want to create an exchange, they seem to deadlock since the test never runs to completion (if no r/w deadlines are set). For example, I want the client to send a message to the server, the server to copy that message and send it back to the client, and then the client to verify that the received message was identical. Here is my test code:

func TestSendAwait(t *testing.T) {
    m := "Hello World"

    go func() {
        conn, err := net.Dial("tcp", testingAddr)
        if err != nil {
            t.Fatal(err)
        }
        defer conn.Close()
        t.Log("client connected to server") // DEBUG

        conn.SetDeadline(time.Now().Add(2 * time.Second))
        conn.Write([]byte(m))

        conn.SetDeadline(time.Now().Add(2 * time.Second))
        buf, err := ioutil.ReadAll(conn)
        if err != nil {
            t.Fatal(err)
        }
        t.Log(string(buf))
    }()

    ln, err := net.Listen("tcp", testingAddr)
    if err != nil {
        t.Fatal(err)
    }
    defer ln.Close()
    t.Log("server started") // DEBUG

    conn, err := ln.Accept()
    if err != nil {
        t.Fatal(err)
    }
    defer conn.Close()
    t.Log("server received connection") // DEBUG

    buf, err := ioutil.ReadAll(conn)
    if err != nil {
        t.Fatal(err)
    }
    t.Logf("server read buffer: %v", buf) // DEBUG

    _, err = conn.Write(buf)
    if err != nil {
        t.Fatal(err)
    }
    t.Log("server wrote to connection") // DEBUG
}

The deadlines are set on the connection because otherwise the deadlock would be indefinite. The output is as follows:

    transmission_test.go:42: server started
    transmission_test.go:24: client connected to server
    transmission_test.go:49: server received connection
    transmission_test.go:32: read tcp 127.0.0.1:41164->127.0.0.1:9090: i/o timeout
    transmission_test.go:55: server read buffer: [72 101 108 108 111 32 87 111 114 108 100]
    transmission_test.go:61: server wrote to connection

Process finished with exit code 1

I don't understand why the client is unable to read and exits, and only then the server decides to send data down the socket? This happens even if I increase the read deadline in the client.

  • 写回答

1条回答 默认 最新

  • dongou1970 2018-03-31 23:53
    关注

    The program blocks on the call to ioutil.ReadAll. This function reads until io.EOF or some other error is returned.

    One fix is to shutdown write after writing data to the connection. This will cause read on the peer to return io.EOF and for ioutil.ReadAll to return successfully.

        conn.Write(data)
        cw, ok := conn.(interface{ CloseWrite() error })
        if !ok {
            // handle error
        }
        cw.CloseWrite()
    

    playground example

    The program in the question does not guarantee that the listener is opened before the connection is dialed or that client will print print the received message. The playground example corrects these issues.

    Another approach is to frame the messages in some way:

    • Write newline or some other byte sequence not allowed in message after message. Read until this byte sequence is found.
    • Write message length before message. Read length and then specified number of bytes.
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?