Golang中NewRequest返回"443 connect: bad file descriptor"错误如何解决

Golang中NewRequest返回"443 connect: bad file descriptor"错误如何解决 我有一个封装了 http.NewRequest 的方法:

func Request(url string, token string, params map[string]string) (*http.Response, error) {
	client := &http.Client{
		Timeout: time.Second * 15,
	}
	
	req, err := http.NewRequest(http.MethodGet, url, nil)
	q := req.URL.Query()
	for i, v := range params {
		q.Add(i, v)
	}
	req.URL.RawQuery = q.Encode()

	req.Header.Set("PRIVATE-TOKEN", token)
	req.Header.Set("Content-Type", "application/json")
	if err != nil {
		return nil, err
	}
	return client.Do(req)
}
params := map[string]string{
    "membership": "true",
    "statistics": "true",
}

resp, err := gitlabutils.Request("https://somewebsite.com", token, params)

有什么原因会导致 err 是:

error="Get \"https://somewebsite.com?membership=true&statistics=true\": dial tcp [2606:4700:90:0:f22e:fbec:5bed:a9b9]:443: connect: bad file descriptor" 

什么会导致 connect: bad file descriptor?有什么想法吗?

谢谢


更多关于Golang中NewRequest返回"443 connect: bad file descriptor"错误如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你的代码是正确的,这个问题似乎发生在套接字通信中。我看到你的主机解析到了IPv6地址。你是否可能在操作系统上关闭了IPv6?

更多关于Golang中NewRequest返回"443 connect: bad file descriptor"错误如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这个错误通常与网络连接或文件描述符限制有关。以下是可能导致此问题的原因和解决方案:

主要问题分析

1. IPv6连接问题

从错误信息看,代码尝试连接IPv6地址 [2606:4700:90:0:f22e:fbec:5bed:a9b9],但可能系统或网络环境不支持IPv6。

解决方案:强制使用IPv4

func Request(url string, token string, params map[string]string) (*http.Response, error) {
    // 创建自定义Transport强制使用IPv4
    transport := &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
            DualStack: false, // 禁用双栈,优先使用IPv4
        }).DialContext,
        ForceAttemptHTTP2:     true,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
    
    client := &http.Client{
        Timeout:   time.Second * 15,
        Transport: transport,
    }
    
    req, err := http.NewRequest(http.MethodGet, url, nil)
    if err != nil {
        return nil, err
    }
    
    q := req.URL.Query()
    for i, v := range params {
        q.Add(i, v)
    }
    req.URL.RawQuery = q.Encode()
    
    req.Header.Set("PRIVATE-TOKEN", token)
    req.Header.Set("Content-Type", "application/json")
    
    return client.Do(req)
}

2. 文件描述符限制

系统可能达到了文件描述符限制。

检查当前限制:

import (
    "fmt"
    "syscall"
)

func checkFileDescriptorLimit() {
    var rlimit syscall.Rlimit
    err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
    if err != nil {
        fmt.Printf("获取限制失败: %v\n", err)
        return
    }
    fmt.Printf("当前文件描述符限制: 软限制=%d, 硬限制=%d\n", 
        rlimit.Cur, rlimit.Max)
}

3. 连接泄漏

确保正确关闭响应体:

resp, err := gitlabutils.Request("https://somewebsite.com", token, params)
if err != nil {
    // 处理错误
    return
}
defer resp.Body.Close() // 必须关闭响应体

// 处理响应
body, err := io.ReadAll(resp.Body)
if err != nil {
    // 处理错误
    return
}

4. 完整修复示例

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

func Request(url string, token string, params map[string]string) (*http.Response, error) {
    // 自定义DNS解析器,优先使用IPv4
    dialer := &net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: false,
    }
    
    transport := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            // 强制使用tcp4而不是tcp
            if network == "tcp" {
                network = "tcp4"
            }
            return dialer.DialContext(ctx, network, addr)
        },
        TLSHandshakeTimeout: 10 * time.Second,
        IdleConnTimeout:     90 * time.Second,
    }
    
    client := &http.Client{
        Timeout:   time.Second * 15,
        Transport: transport,
    }
    
    req, err := http.NewRequest(http.MethodGet, url, nil)
    if err != nil {
        return nil, err
    }
    
    q := req.URL.Query()
    for i, v := range params {
        q.Add(i, v)
    }
    req.URL.RawQuery = q.Encode()
    
    req.Header.Set("PRIVATE-TOKEN", token)
    req.Header.Set("Content-Type", "application/json")
    
    return client.Do(req)
}

5. 环境变量解决方案

也可以通过设置环境变量强制使用IPv4:

// 在程序启动时设置
import "os"

func init() {
    os.Setenv("GODEBUG", "netdns=go")
    os.Setenv("GODEBUG", "netdns=go ipv4=1")
}

关键点:

  1. 错误检查顺序有问题 - 应该在创建请求后立即检查 err
  2. 优先使用IPv4可以避免大多数IPv6相关问题
  3. 确保正确管理HTTP连接和资源释放

这个错误通常不是代码逻辑问题,而是网络环境或系统配置问题。强制使用IPv4是最直接的解决方案。

回到顶部