Go语言中,获取IP地址一般操作是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 获取本机网卡IP
func getLocalIP() (ipv4 string, err error) {
var (
addrs []net.Addr
addr net.Addr
ipNet *net.IPNet // IP地址
isIpNet bool
)
// 获取所有网卡
if addrs, err = net.InterfaceAddrs(); err != nil {
return
}
// 取第一个非lo的网卡IP
for _, addr = range addrs {
// 这个网络地址是IP地址: ipv4, ipv6
if ipNet, isIpNet = addr.(*net.IPNet); isIpNet && !ipNet.IP.IsLoopback() {
// 跳过IPV6
if ipNet.IP.To4() != nil {
ipv4 = ipNet.IP.String() // 192.168.1.1
return
}
}
}

err = ERR_NO_LOCAL_IP_FOUND
return
}

这里会有一个问题,在多网卡(例如有虚拟网卡的情况下),有时会取到无法使用的IP地址,这样程序启动会报错。

这里出现的无法使用的IP地址是什么呢?

我这里指代为链路本地地址link-local unicast address

维基百科:

链路本地地址(Link-local address),又称链接本地地址计算机网络中一类特殊的地址, 它仅供于在网段,或广播域中的主机相互通信使用。这类主机通常不需要外部互联网服务,仅有主机间相互通讯的需求。

IPv4链路本地地址定义在169.254.0.0/16地址块。 IPv6定义在fe80::/10地址块。

所以,虚拟网卡属于这一类。我们需要排除这一类IP地址,我们这里需要判断net flag,就像我们判断IsLoopback一样。

1
2
3
4
5
if ipNet, isIpNet = addr.(*net.IPNet); isIpNet && !ipNet.IP.IsLoopback() {

// 改为

if ipNet, isIpNet = addr.(*net.IPNet); isIpNet && !ipNet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {

将属于链路本地地址的IP,全部拔除。


若是需要获取到机器现在使用的主IP地址,这需要怎么获得呢?

这里可以使用以下程序获取到当前机器的主IP,由于使用的是UDP协议,不会需要握手,所以不需要担心访问不到8.8.8.8:8(或者这样说,这里随便填一个IP地址都可以,反正不会去访问)。

1
2
3
4
5
6
7
conn, err := net.Dial("udp", "8.8.8.8:8")
if err != nil {
panic(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
fmt.Println(localAddr.String())