处理网络数据包读取超时后的错误

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

Handling errors following net packet read timeout

问题

我正在使用conn.SetReadDeadline方法为conn设置读取超时时间。当conn.Read等待的时间超过指定的时间时,它将返回并返回一个类型为*net.OpError的错误。这个错误是在包装所有非io.EOF错误之后由net包返回的。

我可以在包装之前使用Unwrap()获取错误。超时错误是*poll.DeadlineExceededError类型的错误。我在我的代码中使用类似这样的语句来精确处理超时错误。

import "internal/poll"

_, err = conn.Read(p)

if err != nil {
	if pe, ok := err.(*net.OpError); ok {
		err = pe.Unwrap()
		if timeout, ok := err.(*poll.DeadlineExceededError); ok {
			log.Error(fmt.Sprintf("%T, %s", timeout, timeout))
		}
	}
	return
}

在运行程序时,我收到了一个use of internal package internal/poll not allowed的错误。编译器告诉我不能使用内部包。

我通过谷歌搜索找到了一个解决方案,即删除internal文件夹,这是最终解决方案吗?是否会有更好的解决方案?

英文:

I am using the conn.SetReadDeadline method to set the read timeout for conn, When conn.Read waits for more than the specified time, it will return and return an error of type *net.OpError. This error is returned by the net package after wrapping all non-io.EOF errors.

I can get the error before wrapping with Unwrap(). A timeout error is an error of type *poll.DeadlineExceededError. I use statements like this in my code to handle timeout errors precisely.

import "internal/poll"

_, err = conn.Read(p)

	if err != nil {
		if pe, ok := err.(*net.OpError); ok {
			err = pe.Unwrap()
			if timeout, ok := err.(*poll.DeadlineExceededError); ok {
				log.Error(fmt.Sprintf("%T, %s", timeout, timeout))
			}
		}
		return
	}

I received an use of internal package internal/poll not allowed error while running the program. The compiler tells me that internal packages cannot be used.

I googled and found a solution to delete the internal folder, is this the final solution? Will there be a better solution?

答案1

得分: 3

os包将该错误导出为os.ErrDeadlineExceeded(请查看源代码)。你可以尝试使用以下代码:

if errors.Is(err, os.ErrDeadlineExceeded) {
    log.Error("Timeout error")
}

[编辑] 实际上,在阅读了@Brit的评论后,这是检查该错误的文档化方法。请参阅Conn.SetDeadline()的文档:

> 如果超过截止时间,对Read或Write或其他I/O方法的调用将返回一个包装了os.ErrDeadlineExceeded的错误。可以使用errors.Is(err, os.ErrDeadlineExceeded)进行测试。


另一个指示错误是“超时”错误的方法是,如果它具有一个Timeout() bool方法,在调用时返回true

这是net.Error接口的一部分(尽管该接口有一个额外的方法,被记录为已弃用),并由net.OpError类型(以及内部的poll.DeadlineExceededError类型)实现。

有几个函数(例如net.OpError.IsTimeout()os.SyscallError.IsTimeout()和公共函数os.IsTimeout())通过直接将错误值转换为timeout接口来实现超时检查。

如果你想处理可以相互包装的错误,你可能希望使用errors.As(...)来实现自己的isTimeout()检查:

// isTimeout:使用errors.As()来解包错误并检查子错误是否为超时错误
func isTimeout(err error) bool {
    var terr interface{ Timeout() bool }
    return errors.As(err, &terr) && terr.Timeout()
}

https://go.dev/play/p/OhhKY3XsGjZ

请注意:

上面的isTimeout()函数与errors.Is(err, os.ErrDeadlineExceeded)调用之间的区别在于,后者将尝试精确匹配ErrDeadlineExceeded(这主要是通过在某些类似文件或连接的对象上设置SetDeadline()触发的),而后者可能对试图以其他原因宣告“我是超时错误”的错误返回true(例如:HTTP的“408 Request timeout”响应)


*注意:*上面的所有链接都指向go 1.18.3。根据你使用的go版本,你可能需要调整上面的一些代码(例如:os.ErrDeadlineExceeded是在go1.15中添加的)

英文:

The os package exports that error as os.ErrDeadlineExceeded (check the source code). You can try :

if errors.Is(err, os.ErrDeadlineExceeded) {
    log.Error("Timeout error")
}

[edit] actually, after reading @Brit's comment, this is the documented way to check for that error. See the doc for Conn.SetDeadline() :

> If the deadline is exceeded a call to Read or Write or to other
I/O methods will return an error that wraps os.ErrDeadlineExceeded.
This can be tested using errors.Is(err, os.ErrDeadlineExceeded).


Another indication that an error is a "timeout" error is if it has a Timeout() bool method, which returns true when called.

This is part of the net.Error interface (although this interface has an extra method, which is documented as deprecated), and implemented by the net.OpError type (and also by the internal poll.DeadlineExceededError type).

There are several functions (net.OpError.IsTimeout(), os.SyscallError.IsTimeout() and the public function os.IsTimeout() for example) that implement a timeout checking by directly casting the error value to a timeout interface.

If you want to deal with errors that can be wrapped one into another, you may want to implement your own isTimeout() check using errors.As(...) :

// isTimeout : use errors.As() to unwrap errors and check if a sub error is a Timeout error
func isTimeout(err error) bool {
	var terr interface{ Timeout() bool }
	return errors.As(err, &terr) && terr.Timeout()
}

https://go.dev/play/p/OhhKY3XsGjZ

note that :

the difference between the isTimeout() function above and the errors.Is(err, os.ErrDeadlineExceeded) call is that the latter will try to match exacly an ErrDeadlineExceeded (which is mostly triggered by setting SetDeadline() on some file-or-conn-like object), while the latter one may return true for errors that try to advertise "I'm a timeout error" for other reasons (e.g : an HTTP "408 Request timeout" response)


note : all links above refer to go 1.18.3 . Depending on the version of go you are using, you may have to adapt some of the code above (for example : the os.ErrDeadlineExceeded was added in go1.15 )

huangapple
  • 本文由 发表于 2022年6月16日 11:11:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/72640038.html
匿名

发表评论

匿名网友

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

确定