what errors does net.Conn.Write return

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

what errors does net.Conn.Write return

问题

根据Go文档,net.Conn.Write()函数在无法发送给定切片中的所有字节时会返回一个错误。

我如何知道在这种情况下返回的错误类型是什么?我只需要检查是否有错误,以及 n > 0 和 n < len(p) 吗?或者只检查 n < len(p) 就足够了吗?是否存在无法恢复的错误,其中 n < len(p)?

假设我想发送 X 千兆字节的数据。一个"正确"的Write()循环应该是什么样的?

如果输出缓冲区已满,Write()会简单地阻塞直到它从p中发送了所有内容吗?这样一来,检查 n < len(p) 是否多余?

英文:

According to the go documentation, the net.Conn.Write() function will return an error, if it could not send all bytes in the given slice.

How do I know which type of error is returned in this case? Should I just check if there is an error, and if n > 0 and n < len(p) ? Or is it enough to just check if n < len(p) alone? Are there unrecoverable errors for which n < len(p) ?

Say I want to send X gigabytes of data for example. What would a "correct" Write() loop look like?

If the output buffers are full, will Write() simply just block until it has sent everything from p? making a check for n < len(p) superfluous?

答案1

得分: 2

好的,以下是翻译好的内容:

net.Conn 是一个接口。这意味着完全由实现决定要发送哪些错误。TCP 连接和 UNIX 套接字连接可能有非常不同的原因导致写操作无法完全完成。

net.Conn.Write 的签名与 io.Writer 的签名完全相同。这意味着每个 net.Conn 的实现也实现了 io.Writer。因此,您可以使用任何现有的方法,如 io.Copyio.CopyN,将数据写入连接。

英文:

Well, net.Conn is an interface. This means that it is completely up to the implementation to determine which errors to send back. A TCP connection and UNIX socket connection can have very different reasons why a write can't be fully completed.

The net.Conn.Write signature is exactly the same as the io.Writer signature. Which means that every implementation of net.Conn also implements io.Writer. So you can use any existing method like io.Copy or io.CopyN to write data to a connection.

答案2

得分: 1

如何确定在这种情况下返回的错误类型?我应该只检查是否有错误,以及 n > 0 和 n < len(p) 吗?

使用 n < len(p) 来检测写入提前停止的情况。在这种情况下,错误不为 nil。

是否存在不可恢复的错误,使得 n < len(p)?

是的。网络连接在写入一些数据后可能会失败。

还有可恢复的错误。如果写入失败是因为超过了写入截止时间,稍后使用新的截止时间进行写入可能会成功。

假设我想发送 X 千兆字节的数据,一个“正确”的 Write() 循环应该是什么样子?

如果你想知道如何将 []byte 写入连接,大多数情况下不需要循环。Write 调用会阻塞,直到数据被写入或发生错误。使用以下代码:

_, err := c.Write(p)

如果你想知道如何将 io.Reader 复制到网络连接中,请使用 _, err := io.Copy(conn, r)

Write 和 Read 是不同的。Read 可能在填充缓冲区之前返回。

如果输出缓冲区已满,Write() 会一直阻塞直到将 p 中的所有数据发送完毕吗?

Write 会阻塞,直到所有数据被发送或写入失败并返回错误(截止时间超过、网络故障、网络连接在其他 goroutine 中关闭等)。

英文:

> How do I know which type of error is returned in this case? Should I just check if there is an error, and if n > 0 and n < len(p) ?

Use n &lt; len(p) to detect the case where the write stopped early. The error is not nil in this case.

> Are there unrecoverable errors for which n < len(p) ?

Yes. A network connection can fail after some data is written.

There are also recoverable errors. If write fails because the write deadline is exceeded, a later write with a new deadline can succeed.

> Say I want to send X gigabytes of data for example. What would a "correct" Write() loop look like?

If you are asking how to write a []byte to a connection, then a loop is not needed in most cases. The Write call blocks until the data is written or an error occurs. Use this code:

_, err := c.Write(p)

If you are asking how to copy an io.Reader to a network connection, the use _, err := io.Copy(conn, r).

Write is different from Read. Read can return before filling the buffer.

> If the output buffers are full, will Write() simply just block until it has sent everything from p?

Write blocks until all data is sent or the write fails with an error (deadline exceeded, network failure, network connection closed in other goroutine, ...).

答案3

得分: 1

所以,我开始查看Go源代码本身。我追踪了Write()调用到一个名为src/internal/poll/fd_unix.go的文件中。

// Write实现了io.Writer接口。
func (fd *FD) Write(p []byte) (int, error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
if err := fd.pd.prepareWrite(fd.isFile); err != nil {
return 0, err
}
var nn int
for {
max := len(p)
if fd.IsStream && max-nn > maxRW {
max = nn + maxRW
}
n, err := ignoringEINTRIO(syscall.Write, fd.Sysfd, p[nn:max])
if n > 0 {
nn += n
}
if nn == len(p) {
return nn, err
}
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue
}
}
if err != nil {
return nn, err
}
if n == 0 {
return nn, io.ErrUnexpectedEOF
}
}
}

这个函数似乎已经处理了重传。所以,Write()确实保证要么发送所有数据,要么发生致命错误,即不可恢复的错误。

在我看来,除了用于记录目的之外,实际上没有必要关心n的值。如果发生错误,那么这个错误已经严重到没有理由尝试重新传输剩余的len(p)-n字节了。

英文:

So, I went digging into the Go source code itself. I tracked the Write() call to a file named src/internal/poll/fd_unix.go.

// Write implements io.Writer.
func (fd *FD) Write(p []byte) (int, error) {
	if err := fd.writeLock(); err != nil {
		return 0, err
	}
	defer fd.writeUnlock()
	if err := fd.pd.prepareWrite(fd.isFile); err != nil {
		return 0, err
	}
	var nn int
	for {
		max := len(p)
		if fd.IsStream &amp;&amp; max-nn &gt; maxRW {
			max = nn + maxRW
		}
		n, err := ignoringEINTRIO(syscall.Write, fd.Sysfd, p[nn:max])
		if n &gt; 0 {
			nn += n
		}
		if nn == len(p) {
			return nn, err
		}
		if err == syscall.EAGAIN &amp;&amp; fd.pd.pollable() {
			if err = fd.pd.waitWrite(fd.isFile); err == nil {
				continue
			}
		}
		if err != nil {
			return nn, err
		}
		if n == 0 {
			return nn, io.ErrUnexpectedEOF
		}
	}
}

This seems to handle the retransmits already. So, Write() does actually guarantee that either everything is sent, or a fatal error occurs, which is unrecoverable.

It seems to me, that there is no need at all, to care about the value of n, other than for logging purposes. If an error ever occurs, it is severe enough that there is no reason to try and retransmit the remaining len(p)-n bytes.

答案4

得分: 0

net.Conn.Write()是实现io.Writer接口的方法,该接口对于错误有以下约定:

如果返回的n < len(p),则Write必须返回非nil的错误。

没有单一正确的写入循环。对于某些情况,了解写入了多少数据可能很重要。然而,对于网络连接,根据这个约定,以下代码应该可以工作:

var err error
for data, done := getNextSegment(); !done && err == nil; data, done = getNextSegment() {
  _, err = conn.Write(data)
}

要保持写入的总字节数:

var err error
written := 0
for data, done := getNextSegment(); !done && err == nil; data, done = getNextSegment() {
  n, err = conn.Write(data)
  written += n
}
英文:

The net.Conn.Write() is to implement the io.Writer interface, which has the following contract regarding errors:

> Write must return a non-nil error if it returns n < len(p).

There is no single correct write loop. For certain cases, it might be important to know how much data was written. However, for network connections, based on this contract, the following should work:

var err error
for data, done := getNextSegment(); !done&amp;&amp;err==nil; data, done = getNextSegment() {
  _,err=conn.Write(data)
}

To keep the total number of bytes written:

var err error
written:=0
for data, done := getNextSegment(); !done&amp;&amp;err==nil; data, done = getNextSegment() {
  n,err=conn.Write(data)
  written+=n
}

huangapple
  • 本文由 发表于 2021年11月3日 23:49:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/69827855.html
匿名

发表评论

匿名网友

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

确定