net.conn read的时候一直等待是怎么回事呢?

go 1.20 系统 Ubuntu22.04
一个简单的监听 tcp 的代码 ,然后在浏览器请求 127.0.0.1:9999, 这个时候浏览器一直没有返回, 到 server 端发现 read 一直在等待。reader.Read (buf [:]) 这句代码一直在等待

package main

import (
    "bufio"
    "fmt"
    "net"
)

// TCP Server端测试
// 处理函数
func process(conn net.Conn) {
    defer conn.Close() // 关闭连接
    for {
        reader := bufio.NewReader(conn)
        var buf [64]byte
        n, err := reader.Read(buf[:]) // 读取数据
        if err != nil {
            fmt.Println("read from client failed, err: ", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到Client端发来的数据:", recvStr)
        conn.Write([]byte(recvStr)) // 发送数据
    }
}

func main() {
    listen, err := net.Listen("tcp", "127.0.0.1:9999")
    if err != nil {
        fmt.Println("Listen() failed, err: ", err)
        return
    }
    for {
        conn, err := listen.Accept() // 监听客户端的连接请求
        fmt.Println(conn)
        if err != nil {
            fmt.Println("Accept() failed, err: ", err)
            continue
        }
        go process(conn) // 启动一个goroutine来处理客户端的连接请求
    }
}

图片
可以看到 第一轮读取了 64 个字节以后 第二轮 read 就一直在等待 没有任何输出。这是为什么呢

阅读 3.7k
3 个回答

你用错了bufio.NewReader。 你read了64字节以后block是因为conn真的没有再发数据给你了,因为conn发的数据都被bufio.Reader读走了,而你又在每次循环后把bufio.Reader丢掉了,所以你只能读到64字节。
具体可以看bufio.Reader的源码。

func process(conn net.Conn) {
    defer conn.Close()              // 关闭连接
    reader := bufio.NewReader(conn) // 修改这行
    for {
        var buf [64]byte
        n, err := reader.Read(buf[:]) // 读取数据
        if err != nil {
            fmt.Println("read from client failed, err: ", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到Client端发来的数据:", recvStr)
        conn.Write([]byte(recvStr)) // 发送数据
    }
}

// It returns the number of bytes read into p.
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// To read exactly len(p) bytes, use io.ReadFull(b, p).
// At EOF, the count will be zero and err will be io.EOF.

修改代码:

buf := make([]byte, 64)
n, err := io.ReadFull(conn, buf) // 读取数据
头像
用户bPc6Ne5
    3
    美国加利福尼亚州硅谷
    新手上路,请多包涵
    n, err := reader.Read(buf[:]) // 读取数据

    这一行会将尽可能多的conn的数据读取到reader的数据缓冲区中,而reader的数据缓冲区默认大小是4096(go 1.20),因为你测试的请求大概率是小于4096的,因此这一行就已经把前端发的请求数据完全读取到reader的缓冲区里了,下一次for循环如果没有新的前端请求,Read就不会返回了。可以提过阅读bufio.Reader的Read函数代码,或者通过进一步调试验证这个问题。

    简单的解决办法如@小屠 所说。

    实际使用中,buf的大小应该调整为略大于你项目中常见的前端请求包大小,以保证读取效率。另外,如果按照目前的代码实现方式,还需要处理分包(两个请求数据边界)的问题,可能还需要额外关注reader缓冲区大小、最大请求包大小等参数。

    推荐问题
    宣传栏