作者:战地上尉 | 来源:互联网 | 2023-09-03 17:17
与大多数语言一样,Go的标准库很全,因为Go的出现本来就是为了网络通信的高并发实现,所以其相关的网络库封装得更简洁,更Readable。
这里来大致介绍几个网络库,掌握了学习方法,那么只要里面有那个功能,你就能找到并快读查阅源码,了解其实现。 net.ResolveIPAddr()
根据域名查找IP地址
不得不感叹Go为开发者考虑良多,godoc这个工具真的很方便!先看下源码。
$ godoc -src net.ResolveIPAddr
func ResolveIPAddr(net, addr string ) (*IPAddr, error) {
if net == "" {
net = "ip"
}
afnet, _, err := parseNetwork(net)
if err != nil {
return nil , err
}
switch afnet {
case "ip" , "ip4" , "ip6" :
default :
return nil , UnknownNetworkError(net)
}
addrs, err := internetAddrList(afnet, addr, noDeadline)
if err != nil {
return nil , err
}
return addrs.first(isIPv4).(*IPAddr), nil
}
我们又从源码中学习了一招:case "ip", "ip4", "ip6"
。switch的一个case直接检测多个值的方法,如果不匹配则执行default中的代码。
可以看到,net和addr形参都接受string类型,而返回IPAddr的指针类型,和error类型的值。
来使用一下:
package main
import (
"fmt"
"net"
)
func main() {
addr, err := net.ResolveIPAddr ("ip" , "www.baidu.com" )
if err != nil {
fmt.Println (err)
os.Exit (1 )
}
fmt.Println (addr.IP )
输出:
注意看ResolveIPAddr
的源码,如果你传给net的参数不是”ip”, “ip4”, “ip6”其中的一个,那么err就不会是nil,而是UnknownNetworkError(net)
,错误的输出信息会是这样的: unknown network tcp
。
net.ParseIP()
检查IP地址格式是否有效
依照惯例,我们来看一下源码,$ godoc -src net ParseIP
:
func ParseIP(s string ) IP {
for i := 0 ; i <len (s); i++ {
switch s[i] {
case '.' :
return parseIPv4(s)
case ':' :
ip, _ := parseIPv6(s, false )
return ip
}
}
return nil
}
IPv4用.
号隔开,IPv6用:
号隔开,所以这个函数的内部又进行了判断其是IPv4还是IPv6。
注意:你不要手动去调用net.parseIPv4
或者net.parseIPv6
,会报如下错误:
cannot refer to unexported name net.parseIPV 4
undefined: net.parseIPV 4
因为Go利用首字母的大小写来限制包外是否可访问,小写的函数或变量在包外无法访问到,就如同Java的public,private修饰符。不过用godoc来获取小写开头的源码是没有问题的。
查看parseIPv4的源码又发现:
func parseIPv4(s string) IP {
// ...
return IPv4(p[0 ], p[1 ], p[2 ], p[3 ])
}
再追溯到IPv4上
func IPv4 (a , b , c , d byte ) IP {
p := make(IP, IPv6len)
copy(p, v4InV6Prefix)
p[12 ] = a
p[13 ] = b
p[14 ] = c
p[15 ] = d
return p
}
我们发现这些函数都返回了IP对象,我们来看一下IP对象的定义:
type IP []byte
其实就是一个自定义的数组切片类型。
IPv4内部用make初始化了一个数组切片,并且指定了元素个数为IPv6len。IPv6len被定义为常量:
const (
IPv6len = 16
)
然后进行将v4InV6Prefix复制到到数组切片p中,copy的用法请自行搜索(注意copy的行为和常人的理解不同):
var v4InV6Prefix = []byte {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff }
至于储存IPv4的数组切片为什么要分配16个元素的大小,又复制给最后四个索引,可以看type IP []byte
的注释:
// An IP is a single IP address, a slice of bytes.
// Functions in this package accept either 4 -byte (IPv4)
// or 16 -byte (IPv6) slices as input.
//
// Note that in this documentation, referring to an
// IP address as an IPv4 address or an IPv6 address
// is a semantic property of the address, not just the
// length of the byte slice: a 16 -byte slice can still
// be an IPv4 address.
type IP []byte
这说了,一个16-byte大小的数组可以仍然作为IPv4地址。创建数组切片slice1 := make([]int, 5)
其初始值都为0。
Go的源码不难,甚至比C简单,而且标准库的设计也非常规范。如果你需要使用更多的功能,可以查看net包的文档。
转自:http://blog.csdn.net/cc7756789w/article/details/51014076