英文:
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?在其他情况下,我一直在做类似于以下的处理:
if err == syscall.EAGAIN {
/* 以不同的方式处理错误 */
}
例如,这种处理方式效果很好,但我不能对syscall.ECONNRESET
这样做。
更新:
因为我迫切需要一个解决方案,暂时我将使用以下非常不好的方法:
if strings.Contains(err.Error(), syscall.ECONNRESET.Error()) {
println("it's a connection reset by peer!")
return
}
英文:
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
if err == syscall.EAGAIN {
/* handle error differently */
}
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:
if strings.Contains(err.Error(), syscall.ECONNRESET.Error()) {
println("it's a connection reset by peer!")
return
}
答案1
得分: 13
你得到的错误具有底层类型*net.OpError
,例如这里构建的。
你应该能够将错误类型断言为其具体类型,像这样:
operr, ok := err.(*net.OpError)
然后访问它的Err
字段,该字段应该对应于你需要的系统调用错误:
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:
operr, ok := err.(*net.OpError)
And then access its Err
field, which should correspond to the syscall error you need:
operr.Err.Error() == syscall.ECONNRESET.Error()
答案2
得分: 13
@zian的回答比被接受的回答更有用,但是在Go 1.13+版本中,最好避免手动解包错误:
if errors.Is(opErr, syscall.ECONNRESET) {
fmt.Println("发现了ECONNRESET")
}
这样做的好处是你还可以更普遍地使用它,比如在以下情况之后:
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:
if errors.Is(opErr,syscall.ECONNRESET) {
fmt.Println("Found a ECONNRESET")
}
This has the benefit that you can also use it more generally, such as after:
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()
函数(在通过网络发送响应时进行写入)如下所示:
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.ResponseWriter.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")
}
}
}
英文:
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:
func (fd *netFD) Write(p []byte) (nn int, err error) {
nn, err = fd.pfd.Write(p)
runtime.KeepAlive(fd)
return nn, wrapSyscallError("write", err)
}
And wrapSyscallError:
func wrapSyscallError(name string, err error) error {
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError(name, err)
}
return err
}
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:
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")
}
}
}
答案4
得分: 1
@zian - 感谢你对João Pinto(和我)的问题提供了一个很好的解决方案:如何匹配它们,以便在发生时不要惊慌失措?
从Go版本1.13开始,一个改进是使用errors.Is函数,它在“底层”上进行错误解包和顺序测试。例如:
if errors.Is(opErr,syscall.ECONNRESET) {
fmt.Println("找到一个ECONNRESET")
}
@SteveCoffman - 补充你的好答案,谢谢!
英文:
@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 :
if errors.Is(opErr,syscall.ECONNRESET) {
fmt.Println("Found a ECONNRESET")
}
@SteveCoffman - adding to your good answer, cheers!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论