4 回答
TA贡献1818条经验 获得超8个赞
嗯,net.Conn
是一个界面。这意味着完全取决于实现来确定要发送回哪些错误。TCP 连接和 UNIX 套接字连接可能有非常不同的原因导致无法完全完成写入。
签名与net.Conn.Write
签名完全相同io.Writer
。这意味着每个实现net.Conn
也实现io.Writer
. 因此,您可以使用任何现有方法,例如io.Copy
或io.CopyN
将数据写入连接。
TA贡献1784条经验 获得超2个赞
在这种情况下,我如何知道返回了哪种类型的错误?我应该检查是否有错误,如果 n > 0 和 n < len(p) ?
用于n < len(p)
检测写入提前停止的情况。在这种情况下,错误不是零。
是否存在 n < len(p) 的不可恢复错误?
是的。写入某些数据后,网络连接可能会失败。
也有可恢复的错误。如果由于超过了写入期限而导致写入失败,则具有新期限的稍后写入可以成功。
例如,假设我想发送 X GB 的数据。“正确”的 Write() 循环会是什么样子?
如果您询问如何将 a 写入[]byte
连接,则在大多数情况下不需要循环。在写入数据或发生错误之前,Write 调用会阻塞。使用此代码:
_, err := c.Write(p)
如果您询问如何将 io.Reader 复制到网络连接,请使用_, err := io.Copy(conn, r)
.
写不同于读。读取可以在填充缓冲区之前返回。
如果输出缓冲区已满,Write() 会阻塞直到它从 p 发送所有内容?
写入阻塞,直到所有数据都发送完毕或写入失败并出现错误(超过最后期限,网络故障,网络连接在其他 goroutine 中关闭,...)。
TA贡献1835条经验 获得超7个赞
因此,我深入研究了 Go 源代码本身。我跟踪了对名为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 && 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 个字节。
TA贡献1828条经验 获得超13个赞
这net.Conn.Write()是为了实现io.Writer接口,该接口具有以下关于错误的约定:
如果返回 n < len(p),则写入必须返回非零错误。
没有一个正确的写循环。对于某些情况,了解写入了多少数据可能很重要。但是,对于网络连接,基于此合同,以下内容应该有效:
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
}
- 4 回答
- 0 关注
- 197 浏览
添加回答
举报