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

多SOCKS5代理下Go并发请求报端口占用错误的原因及解决方法

问题分析与解决

错误原因

Only one usage of each socket address (protocol/network address/port) is normally permitted.
中文释义:通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

出现该错误的核心原因是本地TCP端口耗尽,具体触发因素包括:

  1. 短时间高并发请求:每次循环发起600个请求,每个请求都会新建TCP连接到SOCKS5代理,每个连接占用一个本地端口。当端口被快速耗尽,且处于TIME_WAIT状态的端口未及时释放时,就会触发端口占用冲突。
  2. 连接复用机制未充分利用:
    • getIp函数采用值传递http.Client,虽然复制后的Client仍指向同一*http.Transport,但这种写法不仅多余,还容易导致对连接池的误用;
    • 未配置MaxConnsPerHost参数:仅设置了MaxIdleConnsPerHost(空闲连接上限),但未限制针对目标主机(api.ipify.org)的并发连接数,导致短时间内创建大量新连接,加速端口消耗。
  3. 无限制循环请求:主函数是无限循环批量请求,没有任何速率控制,进一步加剧端口资源的消耗。

解决方法

1. 修复Client传递方式,复用连接池

将getIp的参数改为指针类型,避免不必要的Client复制,确保所有请求复用同一个Client的Transport连接池,减少重复创建连接:

func getIp(client *http.Client, wg *sync.WaitGroup) {
    defer wg.Done()
    req, err := http.NewRequest("GET", "http://api.ipify.org/", nil)
    if err != nil {
        fmt.Println(err)
        return
    }
    req.Header.Set("user-agent", "okhttp/4.9.3")
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(body))
}

同时修改主函数的调用代码:

go getIp(clients[numClient], &wg)

2. 配置Transport的并发连接限制

在http.Transport中添加MaxConnsPerHost参数,限制单个目标主机的并发连接数,避免短时间创建过多连接:

transport := &http.Transport{
    DialContext:           dialContext,
    MaxIdleConns:          100,  // 适当调高全局空闲连接上限
    IdleConnTimeout:       60 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
    MaxIdleConnsPerHost:   10,
    MaxConnsPerHost:       20,   // 限制单个目标主机的并发连接数
}

3. 控制请求速率

在主循环中添加延迟,避免无限制批量请求,给端口释放留足时间:

func main() {
    config := readConfig()
    proxies := config.Proxy[0:4]
    clients := getClients(proxies)
    numClient := 0
    // 按需调整延迟时间,平衡请求效率与端口消耗
    requestDelay := time.Second * 1
    for {
        var wg sync.WaitGroup
        start := time.Now()
        for i := 1; i < 601; i++ {
            if numClient == len(clients) {
                numClient = 0
            }
            wg.Add(1)
            go getIp(clients[numClient], &wg)
            numClient += 1
        }
        wg.Wait()
        fmt.Printf("%.2fs \n", time.Since(start).Seconds())
        time.Sleep(requestDelay)
    }
}

是否需要其他实现方式?

如果上述优化后仍存在端口耗尽问题,可以考虑以下方案:

  • 自定义代理连接池:自行实现针对SOCKS5代理的连接池,直接复用与代理的TCP连接,减少本地端口占用;
  • 调整系统TCP参数:Linux系统可开启net.ipv4.tcp_tw_reuse加速TIME_WAIT端口回收,Windows可修改注册表扩大端口范围(系统级调整需谨慎);
  • 替换为高效HTTP库:使用fasthttp替代标准库net/http,其连接池机制更高效,能更好地控制并发连接数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:55:42