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
相关产品推荐
相关产品推荐

