我正在学习Go"网络编程与Go"作者Jan Newmarch,我注意到他的几乎所有例子都涉及[512]字节作为写入和读取连接的缓冲区。
我试图在线搜索但未能得到答案。我怀疑它可能与i / o有关,但我不确定这个设计背后的确切原因是什么。
有人可以详细说明缓冲区的选择吗?
本书中的一些示例代码:
func handleConn(c net.Conn){
defer c.Close()
var buf [512]byte
for{
n, err := c.Read(buf[0:])
if err != nil{ return }
_, err2 := c.Write(buf[0:])
if err2 != nil{
return
}
}
}
答案 0 :(得分:1)
除了其他人在评论中所说的内容之外,还不是直接答案,而是一些背景知识。
包装文件和套接字的Go类型相对较薄,因为对它们进行的任何Read()
和Write()
调用都会导致执行系统调用(使用套接字,它还有更多功能)棘手,因为他们通过系统提供的轮询器使用异步I / O,例如epoll
,kqueue
,IOCP
等。这意味着通过1字节的块从文件或网络读取是非常无效的。
考虑另一个极端,可以分配100MiB缓冲区并尝试将其传递给Read()
。虽然内核的系统调用很乐意接受这个大小的目的地,但应该注意的是,现代操作系统在大小为64KiB 1 的网络套接字上有内部缓冲区,所以你的Read()
在大多数情况下,呼叫将返回读取这么多数据或更少。这意味着你将浪费大部分缓冲空间。
现在又出现了另一组考虑因素:从应用程序的套接字读取数据的模式是什么?
说,当您将数据从套接字流式传输到打开的文件时,您并不真正关心缓冲(您希望这是其他人的决定选择"对"大小)。对于这种情况,只需使用io.Copy()
(当前(Go 1.6)使用32KiB的内部缓冲区。)
相反,如果您使用TCP作为传输解析某些应用程序级协议,则通常需要以任意固定大小的块读取数据。对于这种情况,最好的模式是将套接字包裹在bufio.Reader
中 - 以对抗"小读取"上面列出的问题 - 然后使用io.ReadFull()
将数据读入您需要的大小的本地数组/切片中(如果可能,请重复使用数组和切片来降低垃圾收集器的压力)。
另一个案例是基于文本的" linewise" SMTP
或HTTP
等协议。
在这些协议中,最大行长度通常是固定的,并且使用协议行的最大大小的缓冲区来处理它们是有意义的。 (但无论如何,要处理这些协议,最好使用net/textproto
标准包。)
关于你的问题本身,我的抨击是512只是一个没有特殊含义的漂亮数字。当你写这样的书时,你必须选择一些价值。
从我对网络阅读的实际工作模式的描述中可以看出,大多数时候你根本就没有处理缓冲的业务 - 让标准工具为你做这件事。当您遇到标准软件包提供的默认问题时,您应该只考虑调整这些内容。
TL; DR
1 当然,我不能说所有操作系统,并且他们有不同的旋钮来调整这些东西,并且"当代"你可能会在一年或更短的时间内开始意味着不同的事情......我仍然认为我的估计与事实非常接近。