io.Copy有时在从套接字读取时为什么会丢失行?

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

Why does io.Copy sometimes lose lines when reading from a socket?

问题

请帮助我理解从套接字读取的观察行为。

在实验中,发送方始终发送相同的以'\n'结尾的行,然后关闭套接字的写端。

以下是接收方的代码,它按预期工作,打印每一行:

	rdr := bufio.NewReader(sock)
	for {
		b, err := rdr.ReadBytes('\n')
		if err != nil {
			break
		}
		fmt.Print(string(b))
	}

然而,以下代码有时会跳过数据块开头的随机行数,只打印剩余部分(n相应地改变,err始终为nil)。

n, err := io.Copy(os.Stdout, sock)

sock是一个自定义类型,它封装了net.TCPConntls.Conn,在代码库中其他地方使用时没有问题。

为什么通过io.Copy读取的开头行有时会丢失?

英文:

Please help me understand the observed behavior of reading from a socket.

In the experiment, the sender always sends the same lines, which end in '\n', then closes the write end of a socket.

This code on a receiving side works as expected, printing each line:

	rdr := bufio.NewReader(sock)
	for {
		b, err := rdr.ReadBytes('\n')
		if err != nil {
			break
		}
		fmt.Print(string(b))
	}

However, this code

n, err := io.Copy(os.Stdout, sock)

sometimes skips a random number of lines from the beginning of the data block, and only prints the rest (n changes accordingly, and err is always nil).

The sock is a custom type, which abstracts net.TCPConn and tls.Conn, which is otherwise used throughout the codebase w/o problems.

Why lines at the beginning of the read by io.Copy are sometimes lost?

答案1

得分: 1

bufio.Reader 实现了对 io.Reader 对象的缓冲。这是什么意思?如果你使用 bufio.Reader 从一个 io.Reader 源读取数据,它可能会从源中读取更多的数据(并将其缓存),而不仅仅是直接从 bufio.Reader 中读取的数据。

这意味着,如果你首先使用 bufio.Readersock 中读取数据,然后使用 io.Copy(),那么可能已经有一些数据从 sock 中读取并存储在 bufio.Reader 的内部缓冲区中,而 io.Copy() 将无法看到并复制这些数据。

你不应该在同一个 io.Reader 源上混合使用它们。如果你必须这样做,那么请确保首先清空 bufio.Reader 的缓冲区,然后再从 sock 中复制数据,像这样:

// 首先清空缓冲区:
n, err := io.Copy(os.Stdout, rdr)
// 处理错误

// 然后继续处理 socket:
n, err = io.Copy(os.Stdout, sock)
// 处理错误
英文:

bufio.Reader implements buffering for an io.Reader object. What does this mean? If you use a bufio.Reader to read from an io.Reader source, it may read more data from its source (and buffer it) than what you read directly from the bufio.Reader itself.

This means if you use a bufio.Reader to read from the sock first, and then you use io.Copy(), there may be some data already read from sock and sitting in the bufio.Reader's internal buffer, which io.Copy() will not see and will not copy.

You shouldn't mix those 2 on the same io.Reader source. If you must, then be sure to first drain the bufio.Reader's buffer, then proceed to copy from sock like this:

// First drain the buffer:
n, err := io.Copy(os.Stdout, rdr)
// Handle error

// Then proceed with the socket:
n, err = io.Copy(os.Stdout, sock)
// Handle error

huangapple
  • 本文由 发表于 2022年1月13日 17:53:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/70694532.html
匿名

发表评论

匿名网友

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

确定