调用 net.Interfaces() 仅获网卡元信息,需对每个 iface 调用 Addrs() 并正确解析 net.IPNet 或 net.IPAddr,过滤回环、关闭、虚拟网卡(如 docker0/veth/br-),校验 IPv4 非零且可达,跨平台不可依赖索引或名称。

用 net.Interfaces() 获取所有网卡但拿不到 IP?
直接调用 net.Interfaces() 只返回网卡元信息(名字、状态、MAC),不带 IP 地址。必须对每个 Interface 再调用 iface.Addrs() 才能拿到地址列表。
常见错误是忽略错误处理,比如某网卡没配置 IPv4 就 panic;或者把 net.IPNet 的 IP 字段误当成“可直接用的地址”——它其实是网络地址(如 192.168.1.0),不是主机地址。
- 务必检查
addr.(*net.IPNet).IP是否为 IPv4 且非零值:ip.To4() != nil && !ip.IsUnspecified() - 跳过回环(
iface.Flags&net.FlagLoopback != 0)和关闭状态(iface.Flags&net.FlagUp == 0)的接口 -
iface.Addrs()可能返回nil或含错误(如权限不足),需判空再类型断言
只取第一个可用 IPv4 地址?小心 Docker 和虚拟网卡干扰
很多示例代码遍历后取 addrs[0],但 Linux 下常有 docker0、vethxxx、br-xxxx 等虚拟网卡排在前面,它们的 IP(如 172.17.0.1)根本不能对外通信。
真实场景要过滤掉明显非物理/非主网卡的名称,而不是依赖顺序。
立即学习“go语言免费学习笔记(深入)”;
- 排除常见虚拟网卡前缀:
docker0、veth、br-、lo、virbr - 优先选带默认路由的网卡(需查
/proc/net/route或用net.RouteTable(),但后者仅 Linux 支持) - 更稳妥的做法:拿到候选 IP 后,尝试
net.Dial("udp", "8.8.8.8:53")并检查本地地址(conn.LocalAddr().(*net.UDPAddr).IP)
net.Interface.Addrs() 返回的地址格式不一致?
Addrs() 返回的是 []net.Addr,实际可能是 *net.IPNet(如 "192.168.1.100/24")或 *net.IPAddr(如 "127.0.0.1")。前者需取 IP.Mask 计算主机位,后者直接可用。
多数情况你只想要“能绑定监听的 IP”,所以统一转成 net.IP 并过滤:
- 对
*net.IPNet:用ipnet.IP.To4()提取 IPv4 主机地址(注意:不是ipnet.IP本身) - 对
*net.IPAddr:直接取ipaddr.IP.To4() - 丢弃 IPv6 地址(除非明确需要),避免
::1或fe80::段干扰
示例片段:
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.To4() != nil {
ip = ipnet.IP.To4()
} else if ipaddr, ok := addr.(*net.IPAddr); ok && ipaddr.IP.To4() != nil {
ip = ipaddr.IP.To4()
}
跨平台行为差异:macOS 和 Windows 下 net.Interface 排序不稳定
Linux 下网卡名常按驱动加载顺序排列(eth0、eth1),而 macOS 的 en0、en1 不代表物理优先级,Windows 的 Ethernet、Wi-Fi 名称更是由系统控制,无法靠索引取“主网卡”。
硬编码索引(如 interfaces[0])在不同机器上结果可能完全不同。
- 永远不要假设第 N 个接口有意义
- macOS 上
en0多数是 Wi-Fi,en1是有线,但 M1/M2 Mac 可能反过来;Windows 名称还受语言影响(中文系统可能是“以太网”) - 唯一可靠依据是 IP 是否可达 + 是否在默认路由路径上,而非接口名或顺序










