Golang中如何处理“use of closed network connection”错误

Golang中如何处理“use of closed network connection”错误 对于 HTTP 的 ListenAndServelistener.Close 操作,会触发监听器产生 “use of closed network connection” 错误。

我链接了相关的问题,其中包含示例代码。我不确定如何以优雅的方式解决这个错误。 有人能帮忙吗?

受影响/包:net。“use of closed network connection”

你使用的 Go 版本是什么 (go version)?

go 1.18.0

源代码


package main

import (
	"net"
	"net/http"
	"time"
)

func main()  {
	listener, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", "0"))
	if nil!=err{
		panic(err)
	}

	httpServer := &http.Server{}
	go func() {
		err :=httpServer.Serve(listener)
		if nil!=err && err!=http.ErrServerClosed && err!=net.ErrClosed{
			panic(err)
		}
	}()

	time.Sleep(time.Second)
	err = listener.Close()
	if nil!=err{
		panic(err)
	}
	time.Sleep(time.Second)
}

错误

panic: accept tcp [::]:64309: use of closed network connection

goroutine 6 [running]:
main.main.func1()
        .../main.go:19 +0xb3
created by main.main
       .../main.go:16 +0x15a
exit status 2

问题:

这种情况下的 panic 是正常的吗?为什么我关闭监听器后,服务器会收到一个不属于 http.ErrServerClosed 的错误? 处理这个错误的正确方法是什么?


更多关于Golang中如何处理“use of closed network connection”错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你的主控制流将首先启动服务器(在单独的 Go 协程中)。 然后它将关闭服务器正在提供服务的套接字(调用 listener.Close())。

查看 waitgroup 示例,了解如何等待其他 Go 协程完成。

更多关于Golang中如何处理“use of closed network connection”错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在 Go 中处理 “use of closed network connection” 错误,关键在于正确区分正常的服务器关闭和意外错误。你的代码中 panic 是因为错误检查逻辑不够完整。

问题分析

当调用 listener.Close() 时,正在运行的 httpServer.Serve(listener) 会立即返回一个错误。这个错误是 net.ErrClosed 类型的,但你的错误检查没有正确处理所有情况。

解决方案

方法1:使用 http.Server.Shutdown()(推荐)

这是最优雅的方式,可以优雅地关闭 HTTP 服务器:

package main

import (
    "context"
    "net"
    "net/http"
    "time"
)

func main() {
    listener, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", "0"))
    if err != nil {
        panic(err)
    }

    httpServer := &http.Server{}
    go func() {
        err := httpServer.Serve(listener)
        // 服务器正常关闭时,Serve 会返回 ErrServerClosed
        if err != nil && err != http.ErrServerClosed {
            panic(err)
        }
    }()

    time.Sleep(time.Second)
    
    // 使用 Shutdown 优雅关闭服务器
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    if err := httpServer.Shutdown(ctx); err != nil {
        panic(err)
    }
    
    time.Sleep(time.Second)
}

方法2:更完善的错误检查

如果你需要直接操作 listener,可以这样处理错误:

package main

import (
    "errors"
    "net"
    "net/http"
    "time"
)

func main() {
    listener, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", "0"))
    if err != nil {
        panic(err)
    }

    httpServer := &http.Server{}
    go func() {
        err := httpServer.Serve(listener)
        // 检查多种可能的关闭错误
        if err != nil && 
           err != http.ErrServerClosed && 
           !errors.Is(err, net.ErrClosed) {
            panic(err)
        }
    }()

    time.Sleep(time.Second)
    err = listener.Close()
    if err != nil {
        panic(err)
    }
    time.Sleep(time.Second)
}

方法3:使用自定义错误处理

对于更复杂的场景,可以创建错误处理函数:

package main

import (
    "errors"
    "net"
    "net/http"
    "time"
)

func isClosedError(err error) bool {
    if err == nil {
        return false
    }
    
    // 检查标准库错误
    if errors.Is(err, net.ErrClosed) || 
       err == http.ErrServerClosed {
        return true
    }
    
    // 检查错误字符串(作为后备方案)
    errStr := err.Error()
    return errStr == "use of closed network connection" ||
           errStr == "accept tcp: use of closed network connection"
}

func main() {
    listener, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", "0"))
    if err != nil {
        panic(err)
    }

    httpServer := &http.Server{}
    go func() {
        err := httpServer.Serve(listener)
        if err != nil && !isClosedError(err) {
            panic(err)
        }
    }()

    time.Sleep(time.Second)
    err = listener.Close()
    if err != nil {
        panic(err)
    }
    time.Sleep(time.Second)
}

关键点

  1. 使用 http.Server.Shutdown() 是处理 HTTP 服务器关闭的最佳实践
  2. 直接关闭 listener 时,Serve() 方法会返回 net.ErrClosed 错误
  3. Go 1.13+ 推荐使用 errors.Is() 进行错误类型检查
  4. 错误信息可能因 Go 版本和操作系统略有不同

在你的具体案例中,panic 是因为错误检查没有捕获到 net.ErrClosed。使用 errors.Is(err, net.ErrClosed) 可以正确识别这种关闭错误。

回到顶部