
本文介绍如何使用标准库 bufio 和 io 手动读取并逐行打印完整的 HTTP 请求头,适用于从零实现简易 HTTP 服务器的教学场景,避免依赖 net/http 等高级封装。
本文介绍如何使用标准库 `bufio` 和 `io` 手动读取并逐行打印完整的 http 请求头,适用于从零实现简易 http 服务器的教学场景,避免依赖 `net/http` 等高级封装。
在实现一个符合教学要求的轻量级 HTTP 服务器时,关键一步是正确接收并解析客户端发来的原始 HTTP 请求——尤其是完整请求行与所有请求头。由于不能使用 net/http 包,我们必须直接操作 TCP 连接,并借助 bufio.Reader 实现按行读取。
HTTP 请求以空行(即连续两个 \r\n)作为头部结束标志,而每行以 \r\n 结尾(部分客户端可能仅用 \n)。因此,仅调用一次 ReadString('\n') 只能读取首行(如 GET / HTTP/1.1\r\n),后续请求头(如 Host: localhost:8080、User-Agent: ...)将滞留在缓冲区中,导致程序“卡住”或遗漏数据。
正确的做法是:循环调用 ReadString('\n'),直到遇到 io.EOF 或检测到空行为止。注意,io.EOF 在连接关闭时触发;但在 HTTP 协议中,更可靠的头部终止判断是识别空行(即仅含 \r\n 或 \n 的行)。以下是健壮的实现示例:
func handleClient(c net.Conn) {
defer c.Close()
r := bufio.NewReader(c)
for {
line, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
// 客户端主动断开或发送完毕
break
}
log.Printf("read error: %v", err)
return
}
// 去除行尾换行符(兼容 \r\n 和 \n)
line = strings.TrimSpace(line)
fmt.Println("Header:", line)
// 检测空行:HTTP 头部结束标志
if line == "" {
fmt.Println("→ End of headers detected.")
break
}
}
}? 关键说明与注意事项:
-
strings.TrimSpace(line)同时移除\r,\n, 空格和制表符,确保空行被准确识别; - 不应直接
panic(err),教学代码也需体现基础错误处理意识; - 实际 HTTP 服务器还需读取请求体(如 POST 数据),其长度由
Content-Length或Transfer-Encoding决定,本例聚焦头部解析; - 浏览器通常会复用连接(
Connection: keep-alive),若需支持多请求,需在空行后继续读取下一个请求,而非立即关闭连接。
通过该方式,你将能在终端中清晰看到完整的 HTTP 请求头,为后续解析方法、路径、版本及构造响应打下坚实基础。

















