使用 golang 诊断从 unix 套接字读取非常慢(1 分钟对 netcat 中的 1 秒)

Diagnosing very slow read from unix socket using golang (1 min vs 1 sec in netcat)

背景 我写了一些包来与 OpenVas 漏洞扫描器通信——扫描器使用一些不同的propitiatory协议来通信——都是由 xml 或通过 unix 套接字或 tcp 连接发送的文本字符串组成的(我使用 unix 套接字).

我遇到的问题是 OTP 协议(​​没有详细记录的 OpenVas 内部协议)

我可以 运行 使用 netcat 命令执行以下命令,我会在一秒钟内收到回复:

echo -en '< OTP/2.0 >\nCLIENT <|> NVT_INFO\n' | ncat -U /var/run/openvassd.sock

这会导致相当大的响应,在终端中看起来像这样:

< OTP/2.0 >
SERVER <|> NVT_INFO <|> 201802131248 <|> SERVER
SERVER <|> PREFERENCES <|>
cache_folder <|> /var/cache/openvas
include_folders <|> /var/lib/openvas/plugins
max_hosts <|> 30
//lots more here

例如,我以前有一些这样的代码来读取响应:

func (c Client) read() ([]byte, error) {

    // set up buffer to read in chunks
    bufSize := 8096
    resp := []byte{}
    buf := make([]byte, bufSize)

    for {
        n, err := c.conn.Read(buf)
        resp = append(resp, buf[:n]...)
        if err != nil {
            if err != io.EOF {
                return resp, fmt.Errorf("read error: %s", err)
            }
            break
        }
        fmt.Println("got", n, "bytes.")

    }
    fmt.Println("total response size:", len(resp))

    return resp, nil
}

我得到了完整的结果,但它是小块的(我猜是逐行的)所以我看到的输出是这样的(在显示完整响应之前一分钟左右):

got 53 bytes.
got 62 bytes.
got 55 bytes.
got 62 bytes.
got 64 bytes.
got 59 bytes.
got 58 bytes.
got 54 bytes.
got 54 bytes.
got 54 bytes.
got 64 bytes.
got 59 bytes.
... (more)

所以我决定试试 ioutil.ReadAll:

func (c Client) read() ([]byte, error) {
    fmt.Println("read start")
    d, err := ioutil.ReadAll(c.conn)
    fmt.Println("read done")
    return d, err
}

这再次 return 完整响应,但 "read start" 和 "read done" 之间的时间大约为一分钟,而命令预计花费的时间不到 1 秒。

关于为什么与 netcat 相比通过 golang 读取如此慢的任何想法 - 我如何 diagnose/fix 这个问题?**

服务似乎正在等待更多输入,最终在一分钟后超时。在您的 CLI 示例中,一旦 echo 命令完成,管道的那一侧将关闭以进行写入,在这种情况下,服务会收到长度为 0 的 recv 通知。

为了在 Go 中执行相同的操作,您需要在完成发送命令后在 net.UnixConn 上调用 CloseWrite

c.conn.(*net.UnixConn).CloseWrite()