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

如何让Go语言HTTP GET请求仅用IPv4?现有方案无效求助

问题描述

希望让Golang的HTTP GET请求仅使用IPv4,参考相关方案设置了DualStack: false,但请求通过https://ifconfig.me/验证后仍返回IPv6地址,代码示例如下:

package main

import (
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    // 创建Transport对象
    transport := &http.Transport{
        Dial: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
            DualStack: false, // 预期仅使用IPv4
        }).Dial,
    }

    // 创建自定义Transport的HTTP客户端
    client := &http.Client{
        Transport: transport,
    }

    // 创建GET请求
    req, err := http.NewRequest("GET", "https://ifconfig.me/", nil)
    if err != nil {
        fmt.Println("创建请求失败:", err)
        return
    }

    // 发送请求
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("发送请求失败:", err)
        return
    }
    defer resp.Body.Close()

    // 处理响应(省略)
    // ...
}
问题原因与解决方案

核心原因

你的代码使用了Dial字段,但Go 1.13及以后版本中,http.Transport优先调用DialContext而非Dial,导致DualStack: false的配置未生效。此外,仅依赖DualStack参数可能无法完全过滤IPv6,需从DNS解析或连接协议层面强制IPv4。

修正方案

以下是两种可靠的实现方式:

方式一:自定义DialContext强制IPv4解析

在DNS解析阶段仅筛选IPv4地址,确保连接仅使用IPv4:

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    transport := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            // 拆分地址的主机与端口
            host, port, err := net.SplitHostPort(addr)
            if err != nil {
                return nil, err
            }
            // 仅解析IPv4地址
            ips, err := net.LookupIP(host)
            if err != nil {
                return nil, err
            }
            var ipv4Addr net.IP
            for _, ip := range ips {
                if ip.To4() != nil {
                    ipv4Addr = ip
                    break
                }
            }
            if ipv4Addr == nil {
                return nil, fmt.Errorf("未找到%s的IPv4地址", host)
            }
            // 重新拼接带端口的IPv4地址
            addr = net.JoinHostPort(ipv4Addr.String(), port)
            // 使用标准Dialer建立连接
            d := &net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }
            return d.DialContext(ctx, "tcp4", addr)
        },
    }

    client := &http.Client{
        Transport: transport,
    }

    req, err := http.NewRequest("GET", "https://ifconfig.me/", nil)
    if err != nil {
        fmt.Println("创建请求失败:", err)
        return
    }

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("发送请求失败:", err)
        return
    }
    defer resp.Body.Close()

    // 处理响应
    // ...
}

方式二:强制使用tcp4协议

通过固定连接的网络协议为tcp4,直接限制所有连接使用IPv4:

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    transport := &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
        ForceAttemptHTTP2: true,
    }
    // 替换网络协议为tcp4
    originalDial := transport.DialContext
    transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
        return originalDial(ctx, "tcp4", addr)
    }

    client := &http.Client{
        Transport: transport,
    }

    req, err := http.NewRequest("GET", "https://ifconfig.me/", nil)
    if err != nil {
        fmt.Println("创建请求失败:", err)
        return
    }

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("发送请求失败:", err)
        return
    }
    defer resp.Body.Close()

    // 处理响应
    // ...
}

验证说明

修改后重新请求https://ifconfig.me/,返回地址应为IPv4格式。若仍返回IPv6,需检查本地网络是否存在IPv4转IPv6的代理或运营商转换机制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:31:20