Go net.Resolver.LookupNetIP 怎样只取 IPv4 或 IPv6 结果
双栈服务里,如果连接器明确只支持 IPv4 或 IPv6,直接调用 LookupNetIP 得到一组地址后再“猜”哪一个能用,往往会把地址族选择和连接失败混在一起。更直接的做法是:查询阶段就把 network 设为 ip4 或 ip6;只有确实需要同时保留两类结果时,才用 netip.Addr.Is4 或 Is6 做二次筛选。
官方文档:https://pkg.go.dev/net#Resolver.LookupNetIP
LookupNetIP(ctx, "ip4", host)只请求 IPv4 结果,LookupNetIP(ctx, "ip6", host)只请求 IPv6 结果,"ip"则保留两种地址。返回值是[]netip.Addr,不要把它和net.ParseIP的字符串解析混为一谈。
先用 network 参数决定查询结果
我在给连接器增加 IPv6 兼容时,最先踩到的坑是把 LookupNetIP 当成“先查出所有地址,再由调用方随便过滤”的接口。官方签名已经把地址族选择放在第二个参数里:network 只能使用 ip、ip4 或 ip6。因此,调用方的能力边界如果在查询前已经确定,就应该直接传对应值。

package main
import (
"context"
"fmt"
"net"
)
func lookupFamily(ctx context.Context, resolver *net.Resolver, network, host string) error {
// network 只接受 ip、ip4、ip6;由调用方的连接能力决定地址族。
addrs, err := resolver.LookupNetIP(ctx, network, host)
if err != nil {
// DNS 错误和“查询成功但没有目标地址”要分开处理。
return fmt.Errorf("lookup %s %q: %w", network, host, err)
}
if len(addrs) == 0 {
// 没有可用地址时不要继续拼接目标地址或盲目重试。
return fmt.Errorf("lookup %s %q returned no address", network, host)
}
for _, addr := range addrs {
// netip.Addr 可直接转换成文本,用于后续拨号或记录日志。
fmt.Println(addr.String())
}
return nil
}
这里的重点不是把 ip4 或 ip6 写成字符串常量,而是让这个选择和后续连接策略保持一致。例如只创建 IPv4 socket 时,不要先用 ip 拿到 IPv6 地址,再等连接阶段报错。查询阶段过滤还能让“没有该地址族”更早暴露出来。
保留双栈查询时再筛选返回值
有些场景需要把两种地址都留在内存里:例如连接器会根据运行环境选择顺序,或者要先记录完整 DNS 结果。这时可以传 ip,然后按 netip.Addr 的方法筛选。Is4 和 Is6 表达的是地址语义,比检查字符串里有没有冒号更稳妥。

package main
import (
"context"
"fmt"
"net"
"net/netip"
)
func splitAddresses(ctx context.Context, resolver *net.Resolver, host string) (ipv4, ipv6 []netip.Addr, err error) {
// ip 保留双栈结果,适合把地址选择延后到连接策略层。
addrs, err := resolver.LookupNetIP(ctx, "ip", host)
if err != nil {
return nil, nil, fmt.Errorf("lookup %q: %w", host, err)
}
for _, addr := range addrs {
// 用地址类型方法筛选,不依赖 String 的展示格式。
switch {
case addr.Is4():
ipv4 = append(ipv4, addr)
case addr.Is6():
ipv6 = append(ipv6, addr)
}
}
return ipv4, ipv6, nil
}
二次筛选的代价是调用方要自己处理空切片:只有 IPv4 结果时,IPv6 列表为空是正常情况,不应该被当成 DNS 失败;反过来也一样。若业务必须使用某一地址族,建议把“无结果”转成带有 network 和 host 的错误,让上层决定降级、换地址族还是结束请求。
把地址族选择接到连接逻辑
我最后会把规则压缩成下面三种判断:
| 目标 | 查询参数 | 适合的后续处理 |
|---|---|---|
| 只要 IPv4 | ip4 | 直接遍历返回的 IPv4 地址 |
| 只要 IPv6 | ip6 | 直接遍历返回的 IPv6 地址 |
| 保留双栈 | ip | 用 Is4/Is6 归类,再按策略排序 |
不要用 net.ParseIP 代替这层选择:它解决的是“把一段文本解析成 IP”,并不负责 DNS 查询,也不会告诉你某个主机名应该返回哪一类地址。LookupNetIP 已经返回 netip.Addr,直接使用其地址族方法即可。
常见追问
ip4 和 ip6 可以写成 tcp4、tcp6 吗?
不可以。LookupNetIP 的 network 参数只接受 ip、ip4、ip6;tcp4、tcp6 是拨号等 TCP API 的网络名,不能直接套到这里。
为什么不总是先查 ip 再过滤?
如果调用方从一开始就只支持一种地址族,直接传 ip4 或 ip6 更清楚,也能把地址族约束放在查询入口;只有需要保留双栈候选或自定义排序时,才值得延后筛选。
Go net.Resolver.LookupAddr 怎么做反向 DNS 查询并处理多个主机名
- 上一篇
- Go net.Resolver.LookupAddr 怎么做反向 DNS 查询并处理多个主机名
- 下一篇
- Go LookupTXT 返回多个字符串时怎么拼接 DNS 记录
-
- Golang · Go问答 | 21分钟前 | 标准库 · 性能优化 · Go问答 · Go Reader.Next debug/dwarf DWARF Reader.Seek Entry.Offset
- Go debug/dwarf.Reader.Seek 跳回指定偏移后怎样继续遍历条目
- 188浏览 收藏
-
- Golang · Go问答 | 25分钟前 | go ·
- Go debug/dwarf.LineReader Files 表的动态文件索引解析方法
- 298浏览 收藏
-
- Golang · Go问答 | 36分钟前 | go · 压缩 · 资源管理 · Go 资源释放 compress/flate DEFLATE ReadCloser flate.NewReader
- Go flate.NewReader 返回的 ReadCloser 用完后怎么关闭
- 402浏览 收藏
-
- Golang · Go问答 | 42分钟前 | go · 性能 · 压缩 · Go 流式响应 compress/flate Writer.Flush
- Go compress/flate.Writer.Flush 为什么会影响流式响应延迟
- 165浏览 收藏
-
- Golang · Go问答 | 42分钟前 | HTTP · go · Cookie · Go Cookiejar http.Cookie Jar.Cookies
- Go cookiejar.Jar.Cookies 为什么没有返回某个 Cookie
- 418浏览 收藏
-
- Golang · Go问答 | 44分钟前 | 单元测试 · Cookie · Go问答 · 兼容性 · cookie Go net/http/cookiejar Options.PublicSuffixList 内网测试域名
- Go cookiejar.Options.PublicSuffixList 怎么兼容内网测试域名
- 406浏览 收藏
-
- Golang · Go问答 | 49分钟前 | 单元测试 · Go问答 · 拦截器 Connect Go connectinprocess 进程内传输 RPC测试 消息复制
- Connect Go 的进程内传输怎么测试 RPC 而不监听端口
- 291浏览 收藏
-
- Golang · Go问答 | 53分钟前 | 错误处理 · go · Go net/mail Header.AddressList ErrHeaderNotPresent
- Go net/mail.Header.AddressList 缺少 From 字段时怎样区分空值与错误
- 329浏览 收藏
-
- Golang · Go问答 | 53分钟前 |
- Go csv.ParseError 怎样定位 CSV 出错的行和列
- 319浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 974次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 980次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1095次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1068次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 906次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览
-
- 深入了解Golang网络编程Net包的使用
- 2023-01-23 215浏览
-
- Golang中Interface接口的三个特性
- 2023-01-07 394浏览
-
- go语言中函数与方法介绍
- 2023-01-07 297浏览

