用 net.parseip 判断 ip 合法性需结合 ip.to4() 或 ip.to16() 严格校验,因其对“0x7f.0.0.1”等非标格式也返回非 nil;须先 strings.trimspace,且不校验私有地址或网段有效性。

怎么用 net.ParseIP 判断字符串是不是合法 IP 地址
net.ParseIP 看似简单,但返回 nil 不一定代表“非法”——它对 IPv4 和 IPv6 的宽松解析策略容易让人误判。比如 "127.0.0.1"、"::1"、甚至 "0x7f.0.0.1"(十六进制点分格式)都能成功解析,但后者在多数生产环境里应视为非法输入。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 若需严格校验标准 IPv4/IPv6 格式,别只看是否为
nil,要配合ip.To4() != nil或ip.To16() != nil && ip.To4() == nil进一步区分类型和格式 -
net.ParseIP会自动去掉前后空格,但不会处理中间空白或制表符;传入"192.168.1.1\t"可能意外成功,建议先strings.TrimSpace - 它不校验 IP 是否属于私有网段或保留地址(如
"0.0.0.0"、"255.255.255.255"),这些需额外逻辑判断
为什么 net.ResolveIPAddr 有时返回多个结果,有时 panic
调用 net.ResolveIPAddr("ip", "google.com") 返回的是单个地址,但实际 DNS 查询可能返回 A + AAAA 记录;Go 默认只取第一个可用结果(按底层 getaddrinfo 行为),而你真正想要的可能是全部 IP 列表。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 想拿到所有解析结果,请用
net.LookupIP而不是net.ResolveIPAddr;前者返回[]string,后者只返回一个*net.IPAddr -
net.ResolveIPAddr在 hostname 解析失败时返回 error,但若传入空字符串或含非法字符(如"foo..com"),会直接 panic —— 这个行为在文档里没明说,容易在线上崩掉 - 注意网络超时:该函数不接受
context.Context,如需控制超时,得包装在net.Resolver实例里并设置PreferGo: true和自定义Dial
如何正确解析 CIDR 并判断 IP 是否在网段内
net.ParseCIDR 是唯一正统入口,但它返回的 *net.IPNet 对象自带陷阱:IPv4 和 IPv6 的掩码长度含义不同,且 IPNet.Contains 对 nil 输入不设防。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 解析后务必检查返回 error;常见错误是掩码超出范围(IPv4 > 32,IPv6 > 128)或地址格式与掩码不匹配(如
"192.168.1.1/64") -
IPNet.Contains接收net.IP类型,但如果你传的是从net.ParseIP得来的值,要注意它可能是 IPv4 映射成的 IPv6 格式(如::ffff:192.168.1.1),此时直接比对会失败 - 想做精确网段包含判断(比如排除主机位全 0/全 1 的地址),不能只靠
Contains,得手动提取网络地址再比较:ip.Mask(ipNet.Mask).Equal(ipNet.IP)
用 net.InterfaceAddrs 获取本机 IP 时,为什么总拿到 127.0.0.1 或 ::1
这个函数返回的是所有接口的地址列表,包括 loopback;但顺序无保证,不同系统、不同 Go 版本下,lo、en0、wlan0 的排列顺序都可能变化,直接取第一个基本不可靠。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 过滤掉回环地址:
!addr.IP.IsLoopback(),但注意IsLoopback对 IPv6 的::1有效,对某些 IPv4 映射地址(如::ffff:127.0.0.1)可能失效,建议同时检查addr.IP.To4() != nil && addr.IP.To4().IsLoopback() - 避免依赖接口名(如硬写
"eth0"),Linux 下可能是"ens33",macOS 是"en0",Windows 是"Ethernet";更稳的方式是遍历net.Interfaces()并检查Flags & net.FlagUp != 0 && Flags & net.FlagLoopback == 0 - 如果只要 IPv4 地址,记得用
addr.IP.To4() != nil过滤,否则可能混入 IPv6 链路本地地址(如fe80::...),这类地址不能用于跨主机通信
真正难的不是调哪个函数,而是每个函数背后隐含的协议细节、平台差异和边界 case —— 比如 net.ParseCIDR 在 IPv6 下对前导零的容忍度、net.LookupIP 在 /etc/hosts 优先级上的行为、甚至 net.InterfaceAddrs 在容器环境下可能根本拿不到 eth0。这些地方不踩一遍坑,光看文档很难意识到。










