Golang中conn.Read()仅在使用make([]byte,length)时有效而直接使用[]byte无效

Golang中conn.Read()仅在使用make([]byte,length)时有效而直接使用[]byte无效 你好! 我有一个在Go中使用套接字的程序。当我尝试使用以下代码片段从连接读取字节时,它不起作用:

buffer := []byte{}
_, err := conn.Read(buffer)
if err != nil {
	fmt.Println(err)
	break
}

但只有当我这样做时才有效:

buffer := make([]byte, 2048)
_, err := conn.Read(buffer)
if err != nil {
	fmt.Println(err)
	break
}

我以为切片本质上是动态的,所以内存分配应该由系统处理。


更多关于Golang中conn.Read()仅在使用make([]byte,length)时有效而直接使用[]byte无效的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

在这种情况下,需要通过分配一个你选择长度的缓冲区来告诉系统你想要读取多少字节。这与其他语言中的套接字API是一致的。

更多关于Golang中conn.Read()仅在使用make([]byte,length)时有效而直接使用[]byte无效的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


想象一下,如果它是动态的,当底层请求大量内存时,那么你抱怨的就是底层设计问题(例如读取大文件,它会一次性将所有内容加载到内存中),因此将控制权交给调用者比动态更好。

另一方面,make([]byte, 0, 2048) 是一个没有初始化元素的空切片,但在需要重新分配内存之前最多可以追加 2048 个元素。这就是你应该用于缓冲区的做法

更正:对于不确定大小的缓冲区,请使用 bytes.Buffer 而不是切片。

import ("bytes")

buffer := bytes.Buffer

_, err:= buffer.ReadFrom(stream) // stream 是你的连接读取器
if err != nil {
    // 处理错误
}

data := buffer.Bytes()

另一种方法是,如果你已经知道流的大小不会超过某个特定值,可以直接创建一个固定容量的静态数组。

在这种情况下,数组比切片更好,因为它的内存在编译时就已分配。所以当进程运行时,缓冲区空间已经存在,CPU 无需花费时间为一个反正不会增长的切片分配新内存。

动态意味着内存是在运行时动态分配到堆上的,而不是在编译时固定分配的。

切片是动态的,但它们只有在使用 append() 时才会增长,这会告诉 Go 为底层数组预分配内存并使其容量翻倍。不使用 append(),底层数组的大小就不会增加。

[]byte{} 等同于 make([]byte, 0, 1)

其中 0 表示长度,1 表示容量。

这意味着 []byte{} 在空间耗尽前只能存储 1 个字节,但除非你使用 append(),否则它不会自动创建新的底层数组。

请注意 lengthcapacity 之间的区别

make([]byte, 2048) 等同于 make([]byte, 2048, 2048)。它会创建一个包含 2048 个项目的切片,每个项目都初始化为 0。

另一方面,make([]byte, 0, 2048) 是一个空切片,没有初始化的项目,但可以容纳最多 2048 个通过 append 添加的项目,因为不需要内存重新分配。这正是你应该用于缓冲区的情况。

在Go中,conn.Read() 的行为与切片的底层实现有关。当你使用 []byte{}var buffer []byte 创建切片时,你创建的是一个长度和容量都为0的切片。Read 方法会读取数据到切片中,但只能写入到切片长度允许的范围内。由于长度为0,Read 无法写入任何数据,因此会立即返回,而不会阻塞等待数据。

示例代码说明:

// 错误示例:切片长度为0
buffer := []byte{}  // 长度=0, 容量=0
n, err := conn.Read(buffer)  // n 始终为0,不读取任何数据

// 正确示例:使用make预分配长度
buffer := make([]byte, 2048)  // 长度=2048, 容量=2048
n, err := conn.Read(buffer)  // 最多读取2048字节

如果你需要动态缓冲区,可以使用 bytes.Buffer

var buf bytes.Buffer
temp := make([]byte, 256)
for {
    n, err := conn.Read(temp)
    if err != nil {
        break
    }
    buf.Write(temp[:n])
}

或者使用 io.ReadAll(Go 1.16+):

data, err := io.ReadAll(conn)

关键点:Read 方法不会重新分配或扩展切片,它只写入到切片当前长度定义的范围内。这是Go中切片和io.Reader接口的明确设计行为。

回到顶部