Golang如何获取HTTP连接的底层CLIENT socket并读取响应数据
问题根因
- 你调用
httpClient.Do()之后,标准库的http.Transport内部已经完成了响应数据的读取和解析,所有HTTP响应报文已经被从socket缓冲区读走,存到了返回的*http.Response相关结构里,所以你直接从原socket读不到任何剩余数据。 - 你使用的
Dial字段确实已经在Go 1.7之后被标记为弃用,替代方案是使用DialContext字段传入带上下文的拨号函数。
解决方法
场景1:完全自行控制socket读写,只需要标准库帮忙序列化HTTP请求
这种方式最直接,不需要依赖http.Client的响应处理逻辑:
// 1. 建立socket连接 socket, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ipAddress, port)) if err != nil { // 处理连接错误 } defer socket.Close() // 2. 用标准库把http.Request序列化写入socket err = request.Write(socket) if err != nil { // 处理请求写入错误 } // 3. 直接从socket读取原始响应数据自行处理 buf := make([]byte, 4096) for { n, err := socket.Read(buf) if n > 0 { fmt.Print(string(buf[:n])) } if err != nil { break } }
场景2:仍需要http.Client处理重定向、Cookie、鉴权等逻辑,同时要获取原始响应字节
可以通过自定义http.RoundTripper,给拨号返回的连接套一层代理,把读到的响应内容复制一份出来,同时替换弃用的Dial为DialContext:
type TeeConn struct { net.Conn reader io.Reader } func NewTeeConn(conn net.Conn, w io.Writer) *TeeConn { return &TeeConn{ Conn: conn, reader: io.TeeReader(conn, w), } } func (tc *TeeConn) Read(b []byte) (int, error) { return tc.reader.Read(b) } // 调用示例 func main() { // 用来存储原始响应的缓冲区 var rawRespBuf bytes.Buffer httpClient := &http.Client{ Transport: &http.Transport{ // 替换弃用的Dial为DialContext DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { conn, err := net.Dial(network, addr) if err != nil { return nil, err } // 套一层TeeConn,把所有读到的内容复制到rawRespBuf return NewTeeConn(conn, &rawRespBuf), nil }, }, } // 执行请求 res, err := httpClient.Do(request) if err != nil { // 处理请求错误 } defer res.Body.Close() // 此时rawRespBuf里已经存储了完整的原始HTTP响应报文 fmt.Println("原始响应:", rawRespBuf.String()) }
注意事项
- 不要在单连接的Transport中发起多个请求,否则会出现连接复用冲突,导致数据错乱。
- 如果仅需要获取完整响应内容,不需要直接操作socket的话,直接调用
httputil.DumpResponse(res, true)即可拿到完整响应字节,不需要自定义连接。
内容的提问来源于stack exchange,提问作者Andrea Costanzo
相关产品推荐
相关产品推荐

