处理 Go 中的 'connection reset by peer' 错误
原文链接:https://gosamples.dev/connection-reset-by-peer/
这connection reset by peer
是在另一端(对等方)意外关闭连接时发生的TCP/IP错误。
当你从你的一端发送一个数据包,但另一端崩溃并强行关闭与RST
数据包的连接而不是TCP FIN
正常情况下用于关闭连接的连接时,就会发生这种情况。在 Go 中,您可以connection reset by peer
通过检查对等方返回的错误是否等于 来检测syscall.ECONNRESET
。
重现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
查看如何处理
broken pipe
Go post 中的错误,这里会找到另一个生成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
设计有深入的了解。