Handle ‘connection reset by peer’ error in Go

TCP连接在对端异常关闭,并发送RST包过来重建连接时,连接会报connection reset by peer错误,正常关闭连接会发送FIN包。

当连接接收到这类错误时,在Go中可以通过检测连接返回错误是否为syscall.ECONNRESET来判断

重现connection reset by peer错误

重现步骤:

  • 服务端读取一个字节后关闭连接
  • 客户端发送多个字节数据包

当还有剩余数据在连接缓冲中没有读取,服务端关闭连接,则会发送RST数据包给客户端,

客户端尝试从关闭的连接读取数据时,就会返回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
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)
    }

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

    time.Sleep(1 * time.Second) // wait for close on the server side

    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()
}

输出结果:

1
2
2022/07/31 11:25:30 client: read tcp [::1]:62301->[::1]:8080: read: connection reset by peer
2022/07/31 11:25:30 This is connection reset by peer error

注:这只是模拟发生connection reset by peer错误的一种情况,在实际项目中,可能由更多复杂情况导致。

针对不同的情况,需要更加深入了解TCP设计,并结合实际情况分析获得。