You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang使用bufio.Scanner读取HTTP请求无限阻塞的解决方法

问题根因

你当前遇到的阻塞问题核心原因是:bufio.Scanner默认的按行分割规则,会持续等待输入直到TCP连接关闭才会结束扫描。而curl发送完GET请求后不会主动断开连接,会一直等待服务端返回响应,两端就形成了互相等待的死锁。

HTTP 1.1协议规定,请求头和请求体之间以**连续两个CRLF(也就是\r\n\r\n,转换为纯文本就是空行)**作为分隔符,GET请求没有请求体,读到这个空行就代表当前请求所有内容已经传输完毕,可以终止读取。

解决方案

方案1:无需自定义SplitFunc,直接在读行逻辑中加终止判断(推荐,实现最简单)

直接修改request函数,读到空行就主动break扫描循环即可:

func request(conn net.Conn) {
    scanner := bufio.NewScanner(conn)

    for scanner.Scan() {
        ln := scanner.Text()
        fmt.Println(ln)
        // 遇到空行代表请求头结束,GET请求无请求体,直接终止读取
        if ln == "" {
            break
        }
    }
    // 可选:处理扫描错误
    if err := scanner.Err(); err != nil {
        log.Println("读取请求出错:", err)
    }
}

方案2:自定义SplitFunc实现请求结束识别

如果需要通过SplitFunc统一控制扫描终止逻辑,可以实现如下拆分规则:

// 自定义SplitFunc,识别到HTTP请求头结束标记就返回终止
func httpRequestSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
    // 先按默认的行拆分逻辑处理
    advance, token, err = bufio.ScanLines(data, atEOF)
    if err != nil {
        return
    }
    // 如果当前拆分出的行是空字符串,代表遇到了请求结束的空行
    if len(token) == 0 {
        // 返回ErrFinalToken告诉Scanner这是最后一个token,终止扫描
        err = bufio.ErrFinalToken
    }
    return
}

然后在request函数中给scanner指定这个SplitFunc即可:

func request(conn net.Conn) {
    scanner := bufio.NewScanner(conn)
    // 绑定自定义的SplitFunc
    scanner.Split(httpRequestSplitFunc)
    for scanner.Scan() {
        ln := scanner.Text()
        fmt.Println(ln)
    }
    if err := scanner.Err(); err != nil && err != bufio.ErrFinalToken {
        log.Println("读取请求出错:", err)
    }
}

修改后重新运行测试,curl就能正常收到响应了。

注意:如果后续要兼容带请求体的POST/PUT等请求,还需要根据请求头中的Content-Length读取对应长度的请求体内容,再终止读取逻辑。

内容的提问来源于stack exchange,提问作者tanya sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 11:39:05