【问题标题】:Handling connection reset errors in Go处理 Go 中的连接重置错误
【发布时间】:2013-11-12 12:37:00
【问题描述】:

在普通的 Go HTTP 处理程序上,如果我在写入响应的同时断开客户端连接,http.ResponseWritter.Write 将返回错误消息,如write tcp 127.0.0.1:60702: connection reset by peer

现在来自syscall 包,我有sysca.ECONNRESET,其中有消息connection reset by peer,所以它们并不完全相同。

我怎样才能匹配它们,所以我知道如果发生它不要惊慌?在我一直在做的其他场合

if err == syscall.EAGAIN {
  /* handle error differently */
}

例如,效果很好,但我无法使用 syscall.ECONNRESET

更新:

因为我迫切需要一个解决方案,所以我暂时将做这个非常肮脏的黑客:

if strings.Contains(err.Error(), syscall.ECONNRESET.Error()) {
    println("it's a connection reset by peer!")
    return
}

【问题讨论】:

    标签: error-handling go runtime-error


    【解决方案1】:

    例如,您得到的错误具有基础类型*net.OpError,构建here

    您应该能够将错误类型断言到它的具体类型,如下所示:

    operr, ok := err.(*net.OpError)
    

    然后访问它的Err 字段,这应该对应于你需要的系统调用错误:

    operr.Err.Error() == syscall.ECONNRESET.Error()
    

    【讨论】:

      【解决方案2】:

      我遇到了这个问题,接受的答案足以为我指明正确的方向。但是,它提供的用于检查嵌入在 *net.OpError 中的错误是否为 ECONNRESET 的代码不完整,至少对于 Golang 1.9 来说不完整。

      嵌入在OpError.Err 的错误实际上是*os.SyscallError (https://golang.org/pkg/os/#SyscallError) 类型。 struct *net.netFD(通过网络发送响应时写入的内容)实现的 Write() 函数如下所示:

      func (fd *netFD) Write(p []byte) (nn int, err error) {
          nn, err = fd.pfd.Write(p)
          runtime.KeepAlive(fd)
          return nn, wrapSyscallError("write", err)
      }
      

      还有 wrapSyscallError:

      func wrapSyscallError(name string, err error) error {
          if _, ok := err.(syscall.Errno); ok {
              err = os.NewSyscallError(name, err)
          }
          return err
      }
      

      *os.SyscallError 结构中的错误可以直接与 syscall.ECONNRESET 进行比较。

      因此,给定从网络写入返回的错误(例如,调用 http.ResponseWritter.Write),确定该错误是否为 ECONNRESET 的完整代码块是:

      if opErr, ok := err.(*net.OpError); ok {
          if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
              if syscallErr.Err == syscall.ECONNRESET {
                  fmt.Println("Found a ECONNRESET")
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        @zian 的答案比公认的答案更有用,但现在在 Go 1.13+ 上,最好避免手动解包错误:

        if errors.Is(opErr,syscall.ECONNRESET) {
            fmt.Println("Found a ECONNRESET")
        }
        

        这样做的好处是你也可以更一般地使用它,比如在:

        resp, err := http.Get("http://127.0.0.1:4444")
        

        这里这个错误将有一个额外的包装层 (*url.Error),并且会被 @zian 使用的条件错过,而没有第三次显式打开它。

        【讨论】:

        • 只是一个小提示:&syscallErr(作为参数传递给errors.As)可以这样初始化:syscallErr := os.SyscallError{}
        【解决方案4】:

        @zian - 感谢您对 João Pinto(和我的)问题的良好解决方案:我怎样才能匹配它们,所以我知道如果发生这种情况不要惊慌? 在 go 版本 1.13 中,一个改进是使用 errors.Is 函数,该函数在“幕后”按顺序进行错误解包和测试。例如:

        if errors.Is(opErr,syscall.ECONNRESET) {
          fmt.Println("Found a ECONNRESET")
        }
        

        @SteveCoffman - 加入你的好答案,干杯!

        Working with Errors in Go 1.13 - The Go Blog - Golang

        【讨论】:

        • 哦,好点。我现在将其纳入我的答案中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-27
        相关资源
        最近更新 更多