简介

最近使用Gin 框架写接口,总是会出现一些write: connection reset by peer 或者 write: broken pipe 的错误, 在查询资料的时候,发现TCP的下面的情况可以触发一下两种错误。 另外Gin 的出现这个错误的原因这边有个分析Gin-RST 大概原因就是DB 连接池太小,有大量请求排队等待空闲链接,排队时间越长积压的请求越多,请求处理耗时越大,直到积压请求太多把句柄打满,出现了死锁。

write: broken pipe

触发原因:

服务器接收第一个客户端字节并关闭连接。已关闭的服务端 在收到 客户端的下一个字节写入 将导致服务器用 RST 数据包进行应答。当向接收 RSTsocket 发送更多字节时,该socket将返回broken pipe。这就是客户机向服务器发送最后一个字节时发生的情况。

经过测试: 向一个已经关闭的socket 写入数据,(无论buffer 是否写满) 都会出现第一次返回RST, 第二次写入出现broken pipe error, 读的话是EOF

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main

import (
    "errors"
    "log"
    "net"
    "os"
    "syscall"
    "time"
)

func server() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }

    defer listener.Close()

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("server", err)
        os.Exit(1)
    }
    data := make([]byte, 1)
    if _, err := conn.Read(data); err != nil {
        log.Fatal("server", err)
    }

    conn.Close()
}

func client() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Fatal("client", err)
    }

    // write to make the connection closed on the server side
    if _, err := conn.Write([]byte("a")); err != nil {
        log.Printf("client: %v", err)
    }

    time.Sleep(1 * time.Second)

    // write to generate an RST packet
    if _, err := conn.Write([]byte("b")); err != nil {
        log.Printf("client: %v", err)
    }

    time.Sleep(1 * time.Second)

    // write to generate the broken pipe error
    if _, err := conn.Write([]byte("c")); err != nil {
        log.Printf("client: %v", err)
        if errors.Is(err, syscall.EPIPE) {
            log.Print("This is broken pipe error")
        }
    }
}

func main() {
    go server()

    time.Sleep(3 * time.Second) // wait for server to run

    client()
}

connection reset by peer

触发原因: 如果服务器用socket接收缓冲区中剩余的字节关闭连接,那么将向客户端发送一个 RST 数据包。当客户端尝试从这样一个关闭的连接中读取时,它将通过对等错误获得连接重置。

经过测试: 当向一个写满了缓冲区,并关闭的socket 进行read 或者write 操作都会导致connection reset by peer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
    "errors"
    "log"
    "net"
    "os"
    "syscall"
    "time"
)

func server() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }

    defer listener.Close()

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("server", err)
        os.Exit(1)
    }
    data := make([]byte, 2)
    if _, err := conn.Read(data); err != nil {
        log.Fatal("server", err)
    }

    conn.Close()
}

func client() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Fatal("client", err)
    }

    if _, err := conn.Write([]byte("abc")); err != nil {
        log.Printf("client: %v", err)
    }

    time.Sleep(1 * time.Second) // wait for close on the server side
    // 下面的操作第一次read /write 都会 出现ECONNRESET  reset by peer
    // 第二次的读则是EOF, 如果是写则是`write: broken pipe`

    // if _, err := conn.Write([]byte("ab")); err != nil {
    //     log.Printf("client: %v", err)
    // }

    data := make([]byte, 1)
    if _, err := conn.Read(data); err != nil {
        log.Printf("client: %v", err)
        if errors.Is(err, syscall.ECONNRESET) {
            log.Print("This is connection reset by peer error")
        }
    }
}

func main() {
    go server()

    time.Sleep(3 * time.Second) // wait for server to run

    client()
}