
使用 go 的底层 net 包发起 http post 时,若未正确终止请求体或缺失关键头部(如 content-length),服务器将因等待更多数据而挂起,导致请求无响应。解决方法包括显式关闭写入端或手动添加标准 http 头部。
使用 go 的底层 net 包发起 http post 时,若未正确终止请求体或缺失关键头部(如 content-length),服务器将因等待更多数据而挂起,导致请求无响应。解决方法包括显式关闭写入端或手动添加标准 http 头部。
在 Go 中绕过 net/http、直接使用 net 包构建 HTTP 请求虽具教学意义,但也极易因忽略 HTTP 协议细节而失败。你提供的代码存在三个关键问题:
-
缺少必要的 HTTP 请求头:HTTP/1.0 要求至少包含
Host头(RFC 1945 建议),而现代 PHP 服务器(如 Apache、Nginx)通常依赖Content-Length或Transfer-Encoding判断请求体边界; -
未正确结束请求体传输:仅调用
conn.Write()不会向 TCP 连接发送 FIN 包,服务器持续等待后续数据,直到超时; -
读取逻辑错误:
ReadString('z')试图读到字符'z',但 HTTP 响应以\r\n\r\n分隔状态行与正文,且末尾未必含'z',易造成阻塞。
✅ 正确做法是显式关闭写入端(适用于 HTTP/1.0)或补全标准请求头(推荐,兼容性更强):
✅ 方案一:关闭写入端(简单可靠)
conn, err := net.Dial("tcp", "127.0.0.1:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送符合规范的 HTTP/1.0 请求头(含 Host)
_, _ = fmt.Fprintf(conn,
"POST /handleupload.php HTTP/1.0\r\n"+
"Host: 127.0.0.1\r\n"+
"Content-Length: 10\r\n"+
"\r\n")
// 写入原始数据
n, err := conn.Write([]byte("ABCDEFGHIJ"))
if err != nil || n != 10 {
log.Fatal("write failed:", err)
}
// 显式关闭写入方向,通知服务器请求体结束
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.CloseWrite() // 关键:发送 FIN,避免服务器等待
} else {
conn.Close() // 兜底关闭
}
// 安全读取响应(避免 ReadString 阻塞)
reader := bufio.NewReader(conn)
resp, err := http.ReadResponse(reader, &http.Request{Method: "POST"})
if err != nil {
log.Fatal("read response failed:", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Status: %s\nBody: %s\n", resp.Status, string(body))✅ 方案二:完整构造 HTTP/1.1 请求(更健壮)
req := "POST /handleupload.php HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Content-Length: 10\r\n" +
"Connection: close\r\n" + // 显式要求关闭连接
"\r\n" +
"ABCDEFGHIJ"
conn, err := net.Dial("tcp", "127.0.0.1:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, _ = conn.Write([]byte(req))
// 直接读取响应(无需 CloseWrite)
reader := bufio.NewReader(conn)
resp, err := http.ReadResponse(reader, &http.Request{Method: "POST"})
// ... 同上处理 resp⚠️ 注意事项:
-
CloseWrite()仅对*net.TCPConn有效,需类型断言;UDP 或其他连接不支持; - 生产环境强烈建议使用
net/http包——它自动处理头部、连接复用、重定向、超时等; - 手动拼接 HTTP 请求时,务必确保
\r\n换行符严格符合 RFC 规范(CRLF),否则服务器可能拒绝解析; - PHP 脚本需通过
file_get_contents('php://input')读取原始 POST 数据(而非$_POST,后者仅解析application/x-www-form-urlencoded或multipart/form-data)。
总结:底层网络编程需严格遵循协议规范。缺失 Content-Length 或未终止请求流,是导致服务器“收不到数据”的最常见原因。优先使用 net/http;若必须手写,务必补全头部并明确结束请求体传输。

















