英文:
How to check a net.Conn is closed?
问题
或者如何在循环中检查连接是否可用于读取或写入?如果连接关闭或不可用,我们应该停止循环。
例如:
package main
import "net"
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:1111")
defer conn.Close()
for {
buf := make([]byte, 1, 1)
n, err := conn.Read(buf)
if err != nil {
// 当发生任何错误时,我们目前只能停止循环
log.Fatal(err)
}
}
}
英文:
Or how to check it is available for Read
or Write
in loop? If the conn is closed or not available, we should stop the loop.
For example:
package main
import "net"
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:1111")
defer conn.Close()
for {
buf := make([]byte, 1, 1)
n, err := conn.Read(buf)
if err != nil {
// currently we can only stop the loop
// when occur any errors
log.Fatal(err)
}
}
}
答案1
得分: 2
根据连接关闭的方式,可能会出现多种错误。从Read
中唯一可以预期到的错误是io.EOF
。io.EOF
是用来指示连接正常关闭的值。
其他错误可以通过net.Error
接口的Timeout
和Temporary
方法进行检查。这些错误通常是net.OpError
类型。从Write
返回的任何非临时错误都是致命的,因为它表示写入无法成功,但请注意,由于底层网络API的原因,返回没有错误的写入仍然不能保证已成功。
通常情况下,您只需遵循io.Reader
的API。
当Read
在成功读取了n个字节后遇到错误或文件结束条件时,它将返回读取的字节数。它可能会在同一次调用中返回(非nil)错误,也可能在后续调用中返回错误(且n == 0)。这种一般情况的一个实例是,在输入流的末尾返回非零字节数的Reader可能返回err == EOF或err == nil。下一次的Read应该返回0和EOF。
如果有数据被读取,您首先处理该数据。在处理完数据后,您可以在任何错误时跳出循环。如果是io.EOF
,则连接正常关闭,其他任何错误都可以根据需要进行处理。
英文:
You can get a number of errors, depending on how the connection was closed. The only error that you can count on receiving from a Read
is an io.EOF
. io.EOF
is the value use to indicate that a connection was closed normally.
Other errors can be checked against the net.Error
interface for its Timeout
and Temporary
methods. These are usually of the type net.OpError
. Any non-temporary error returned from a Write
is fatal, as it indicates the write couldn't succeed, but note that due to the underlying network API, writes returning no error still aren't guaranteed to have succeeded.
In general you can just follow the io.Reader
api.
> When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF.
If there was data read, you handle that first. After you handle the data, you can break from the loop on any error. If it was io.EOF
, the connection is closed normally, and any other errors you can handle as you see fit.
答案2
得分: 2
如果您使用的是Go 1.16或更新版本,并且仅使用标准库(而不是任意的网络堆栈),您可以使用以下函数来检查关闭错误并与其他错误进行不同的处理:
func isNetConnClosedErr(err error) bool {
switch {
case
errors.Is(err, net.ErrClosed),
errors.Is(err, io.EOF),
errors.Is(err, syscall.EPIPE):
return true
default:
return false
}
}
请注意,如果您处理的是文件,还可以添加os.ErrClosed
错误(fs.ErrClosed
的别名),但是当仅使用net
包时,您不需要添加它。虽然您的代码只显示了一个客户端,但可能还有一个服务器端执行listener.Accept()
,由不同的Go进程关闭,当发生这种情况时,您不希望记录令人讨厌的错误,因此您希望在上面的列表中包含net.ErrClosed
。至于syscall.EPIPE
,当向已关闭连接的远程端写入数据时,它非常有用。请记住,在网络连接中,您可能在第一次写入时不会收到EPIPE错误,因为操作系统可能需要发送一些数据才能发现远程端已关闭。
英文:
If you are using Go 1.16 or newer and only working with the standard libraries (not some arbitrary networking stack), you can use a function like this to check the closed errors and handle them differently than other errors:
func isNetConnClosedErr(err error) bool {
switch {
case
errors.Is(err, net.ErrClosed),
errors.Is(err, io.EOF),
errors.Is(err, syscall.EPIPE):
return true
default:
return false
}
}
Note that there is an os.ErrClosed
error (alias for fs.ErrClosed
) that you could add if you were dealing with files, but you don't need it when only using the net
package. While your code only shows a client, there is probably a server side doing listener.Accept()
that gets closed by a different go process and you don't want to log nasty errors when that happens, so you want to have net.ErrClosed
in the list above. As for syscall.EPIPE
, this comes in handy when writing to a remote end that already closed the connection. Remember that with networking connections, you may not get the EPIPE on the 1st write, as the OS may need to send some data to discover that the remote end was closed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论