处理Go语言中的连接重置错误

huangapple go评论158阅读模式
英文:

Handling connection reset errors in Go

问题

在一个普通的Go HTTP处理程序中,如果在仍然向响应写入数据时断开客户端连接,http.ResponseWriter.Write将返回一个带有类似于write tcp 127.0.0.1:60702: connection reset by peer的错误消息。

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

我该如何匹配它们,以便在发生这种情况时知道不要引发panic?在其他情况下,我一直在做类似于以下的处理:

  1. if err == syscall.EAGAIN {
  2. /* 以不同的方式处理错误 */
  3. }

例如,这种处理方式效果很好,但我不能对syscall.ECONNRESET这样做。

更新:

因为我迫切需要一个解决方案,暂时我将使用以下非常不好的方法:

  1. if strings.Contains(err.Error(), syscall.ECONNRESET.Error()) {
  2. println("it's a connection reset by peer!")
  3. return
  4. }
英文:

On a plain Go HTTP handler, if I disconnect a client while still writting to the response, http.ResponseWritter.Write will return an error with a message like write tcp 127.0.0.1:60702: connection reset by peer.

Now from the syscall package, I have sysca.ECONNRESET, which has the message connection reset by peer, so they're not exactly the same.

How can I match them, so I know not to panic if it occurs ? On other ocasions I have been doing

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

for instance, and that worked fine, but I can't do it with syscall.ECONNRESET.

Update:

Because I'm desperate for a solution, for the time being I'll be doing this very dirty hack:

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

答案1

得分: 13

你得到的错误具有底层类型*net.OpError,例如这里构建的。

你应该能够将错误类型断言为其具体类型,像这样:

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

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

  1. operr.Err.Error() == syscall.ECONNRESET.Error()
英文:

The error you get has the underlying type *net.OpError, built here, for example.

You should be able to type-assert the error to its concrete type like this:

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

And then access its Err field, which should correspond to the syscall error you need:

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

答案2

得分: 13

@zian的回答比被接受的回答更有用,但是在Go 1.13+版本中,最好避免手动解包错误:

  1. if errors.Is(opErr, syscall.ECONNRESET) {
  2. fmt.Println("发现了ECONNRESET")
  3. }

这样做的好处是你还可以更普遍地使用它,比如在以下情况之后:

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

在这里,否则err会有一个额外的包装层(*url.Error),并且会被@zian使用的条件所忽略,除非显式地解包它第三次。

英文:

The answer by @zian is more useful than the accepted answer, but now on Go 1.13+ it is preferable to avoid manually unwrapping the errors:

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

This has the benefit that you can also use it more generally, such as after:

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

Here this err would otherwise have an extra layer of wrapping (*url.Error) and would be missed by the condition @zian used without explicitly unwrapping it a third time.

答案3

得分: 11

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

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

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

以及wrapSyscallError函数:

  1. func wrapSyscallError(name string, err error) error {
  2. if _, ok := err.(syscall.Errno); ok {
  3. err = os.NewSyscallError(name, err)
  4. }
  5. return err
  6. }

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

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

  1. if opErr, ok := err.(*net.OpError); ok {
  2. if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
  3. if syscallErr.Err == syscall.ECONNRESET {
  4. fmt.Println("Found a ECONNRESET")
  5. }
  6. }
  7. }
英文:

I came across this issue and the accepted answer was sufficient to point me in the right direction. However, the code it provides to check if the Error embedded inside *net.OpError is ECONNRESET is not complete, at least not for Golang 1.9.

The error embedded at OpError.Err is actually of type *os.SyscallError (https://golang.org/pkg/os/#SyscallError). The Write() function implemented by struct *net.netFD (which is what's being written to when sending a response over the network) looks like this:

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

And wrapSyscallError:

  1. func wrapSyscallError(name string, err error) error {
  2. if _, ok := err.(syscall.Errno); ok {
  3. err = os.NewSyscallError(name, err)
  4. }
  5. return err
  6. }

The error inside the *os.SyscallError struct can be directly compared against syscall.ECONNRESET.

So, given an error returned from a network write (e.g. a call to http.ResponseWritter.Write), the full code block to determine if that error is ECONNRESET is:

  1. if opErr, ok := err.(*net.OpError); ok {
  2. if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
  3. if syscallErr.Err == syscall.ECONNRESET {
  4. fmt.Println("Found a ECONNRESET")
  5. }
  6. }
  7. }

答案4

得分: 1

@zian - 感谢你对João Pinto(和我)的问题提供了一个很好的解决方案:如何匹配它们,以便在发生时不要惊慌失措?
从Go版本1.13开始,一个改进是使用errors.Is函数,它在“底层”上进行错误解包和顺序测试。例如:

  1. if errors.Is(opErr,syscall.ECONNRESET) {
  2. fmt.Println("找到一个ECONNRESET")
  3. }

@SteveCoffman - 补充你的好答案,谢谢!

在Go 1.13中处理错误 - Go博客 - Golang

英文:

@zian - thanks for your good solution to João Pinto's (and my) question : How can I match them, so I know not to panic if it occurs ?
As at go version 1.13, an improvement is to use the errors.Is function which does error unwrapping and testing sequentially 'under the hood'. For example :

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

@SteveCoffman - adding to your good answer, cheers!

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

huangapple
  • 本文由 发表于 2013年11月12日 20:37:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/19929386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定