connection reset by peer 是一个TCP/IP错误,发生在另一端(对等体)意外关闭连接的情况下。它发生在你从你的一端发送一个数据包,但另一端崩溃并强行关闭了连接与 RST数据包而不是 TCP FIN,这在正常情况下是用来关闭连接的。在Go中,你可以通过检查对方返回的错误是否等于connection reset by peer 。
重现connection reset by peer 的错误
我们可以通过创建一个做以下工作的服务器和客户端来重现这个错误:
- 服务器读取一个字节,然后关闭连接
- 客户端发送了一个以上的字节
如果服务器用套接字接收缓冲区中的剩余字节关闭连接,那么就会向客户端发送一个RST 包。当客户端试图从这样一个关闭的连接中读取时,它将得到connection reset by peer 的错误。
请看下面的例子,它模拟了这种行为。
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()
}
输出
2021/10/20 19:01:58 client: read tcp [::1]:59897->[::1]:8080: read: connection reset by peer
2021/10/20 19:01:58 This is connection reset by peer error
处理connection reset by peer 错误
通常情况下,你可以在客户端向服务器发送的请求中看到connection reset by peer 错误。这意味着服务器发生了一些不好的事情:服务器重启了,程序崩溃了,或者发生了其他问题,导致连接被强行关闭。由于TCP连接可以被破坏,所以不需要在客户端以任何特殊方式处理connection reset by peer 。你可以记录这个错误,忽略它或者在它发生时重试连接。在上面的例子中,我们使用errors.Is() 函数检测错误,检查返回的错误是否是一个syscall.ECONNRESET.
connection reset by peer 和 broken pipe之间的区别
当一个对等体(另一端)意外地关闭底层连接时,connection reset by peer 和broken pipe 错误都会发生。然而,它们之间有一个微妙的区别。通常情况下,当你在服务器发送数据包后从连接中读取时,你会得到connection reset by peer 。 RST数据包后,你会得到,而当你在发送数据包后写到连接时,你会得到。 RST反之,你会得到broken pipe 的错误。
用下面的代码替换上面例子中的client() 函数,以再现broken pipe 错误。
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
if _, err := conn.Write([]byte("b")); err != nil {
log.Printf("client: %v", err)
}
}
使用新的客户端,你会看到输出:
2021/10/20 19:55:40 client: write tcp [::1]:60399->[::1]:8080: write: broken pipe
请注意,这些简单的例子并没有涵盖所有可能发生connection reset by peer 和broken pipe 的情况。你可以看到这些错误的情况要多得多,在什么情况下看到什么错误,需要对TCP设计有深刻的理解。