如何检查 net.Conn 是否已关闭?

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

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.EOFio.EOF是用来指示连接正常关闭的值。

其他错误可以通过net.Error接口的TimeoutTemporary方法进行检查。这些错误通常是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.

huangapple
  • 本文由 发表于 2017年7月7日 23:48:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/44974984.html
匿名

发表评论

匿名网友

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

确定