func LookupHost(name string) (cname string, addrs []string, err os.Error)
func LookupPort(network, service string) (port int, err os.Error)
两个函数都在 src/net/lookup.go中定义。
【LookupHost】
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err error) {
return lookupHost(host)
}
实际运营中,通常主机服务器有多块网卡,LookupHost返回多个IP地址。以string数组形式返回。
在源文件中调用了 lookupHost(host) 私有函数。
lookuphost(host)定义在 lookup_unix.go文件中,源码如下
func lookupHost(host string) (addrs []string, err error) {
addrs, err, ok := cgoLookupHost(host)
if !ok {
addrs, err = goLookupHost(host)
}
return
}
函数主体是个Comma-ok,调用了cgoLookupHost(host)函数,其定义在cgo_unix.go中
源码如下:
func cgoLookupHost(name string) (addrs []string, err error, completed bool) {
ip, err, completed := cgoLookupIP(name)
for _, p := range ip {
addrs = append(addrs, p.String())
}
return
}
cgoLookupHost 直接调用cgoLookupIP(name) 函数,其同样定义在cgo_unix.go中
源码如下
func cgoLookupIP(name string) (addrs []IP, err error, completed bool) {
addrs, _, err, completed = cgoLookupIPCNAME(name)
return
}
同样cgoLookupIP直接调用cgoLookupIPCNAME(name),该部分主要通过调用系统函数 getaddrinfo将一个主机名和一个服务名映射到一个地址。源码如下
func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) {
acquireThread()
defer releaseThread()
var res *C.struct_addrinfo
var hints C.struct_addrinfo
hints.ai_flags = cgoAddrInfoFlags()
hints.ai_socktype = C.SOCK_STREAM
h := C.CString(name)
defer C.free(unsafe.Pointer(h))
gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
if gerrno != 0 {
var str string
if gerrno == C.EAI_NONAME {
str = noSuchHost
} else if gerrno == C.EAI_SYSTEM {
if err == nil {
// err should not be nil, but sometimes getaddrinfo returns
// gerrno == C.EAI_SYSTEM with err == nil on Linux.
// The report claims that it happens when we have too many
// open files, so use syscall.EMFILE (too many open files in system).
// Most system calls would return ENFILE (too many open files),
// so at the least EMFILE should be easy to recognize if this
// comes up again. golang.org/issue/6232.
err = syscall.EMFILE
}
str = err.Error()
} else {
str = C.GoString(C.gai_strerror(gerrno))
}
return nil, "", &DNSError{Err: str, Name: name}, true
}
defer C.freeaddrinfo(res)
if res != nil {
cname = C.GoString(res.ai_canonname)
if cname == "" {
cname = name
}
if len(cname) > 0 && cname[len(cname)-1] != '.' {
cname += "."
}
}
for r := res; r != nil; r = r.ai_next {
// We only asked for SOCK_STREAM, but check anyhow.
if r.ai_socktype != C.SOCK_STREAM {
continue
}
switch r.ai_family {
default:
continue
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
addrs = append(addrs, copyIP(sa.Addr[:]))
case C.AF_INET6:
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
addrs = append(addrs, copyIP(sa.Addr[:]))
}
}
return addrs, cname, nil, true
}
####################
【LookupPort】
函数原型如下,同样定义在lookup.go文件中
// LookupPort looks up the port for the given network and service.
func LookupPort(network, service string) (port int, err error) {
return lookupPort(network, service)
}
直接返回 lookupPort私有函数,其定义在lookuo_unix.go中
源码如下
func lookupPort(network, service string) (port int, err error) {
port, err, ok := cgoLookupPort(network, service)
if !ok {
port, err = goLookupPort(network, service)
}
return
}
函数主体直接调用cgoLookupPort 函数,其定义在cgo_unix.go文件中,同样,cgoLookupPort函数会调用系统接口函数getaddrinfo。其实现源码如下:
func cgoLookupPort(net, service string) (port int, err error, completed bool) {
acquireThread()
defer releaseThread()
var res *C.struct_addrinfo
var hints C.struct_addrinfo
switch net {
case "":
// no hints
case "tcp", "tcp4", "tcp6":
hints.ai_socktype = C.SOCK_STREAM
hints.ai_protocol = C.IPPROTO_TCP
case "udp", "udp4", "udp6":
hints.ai_socktype = C.SOCK_DGRAM
hints.ai_protocol = C.IPPROTO_UDP
default:
return 0, UnknownNetworkError(net), true
}
if len(net) >= 4 {
switch net[3] {
case '4':
hints.ai_family = C.AF_INET
case '6':
hints.ai_family = C.AF_INET6
}
}
s := C.CString(service)
defer C.free(unsafe.Pointer(s))
if C.getaddrinfo(nil, s, &hints, &res) == 0 {
defer C.freeaddrinfo(res)
for r := res; r != nil; r = r.ai_next {
switch r.ai_family {
default:
continue
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
return int(p[0])<<8 | int(p[1]), nil, true
case C.AF_INET6:
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
return int(p[0])<<8 | int(p[1]), nil, true
}
}
}
return 0, &AddrError{"unknown port", net + "/" + service}, true
}
################
【小结】
LookupHost的调用链如下:
func LookupHost(name string) (cname string, addrs []string, err os.Error)
func lookupHost(host string) (addrs []string, err error)
func cgoLookupHost(name string) (addrs []string, err error, completed bool)
func cgoLookupIP(name string) (addrs []IP, err error, completed bool)
func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool)
getaddrinfo(h, nil, &hints, &res)
LookupPort 的调用链如下:
func LookupPort(network, service string) (port int, err error)
func lookupPort(network, service string) (port int, err error)
func cgoLookupPort(net, service string) (port int, err error, completed bool)
getaddrinfo(nil, s, &hints, &res)
即最后都回归到调用系统借口getaddrinfo。其函数原型如下,定义在netdb.h中。
extern int getaddrinfo (__const char *__restrict __name,
__const char *__restrict __service,
__const struct addrinfo *__restrict __req,
struct addrinfo **__restrict __pai);
将一个主机名和一个服务名映射到一个地址,返回一个addrinfo的链表结构。
其定义如下:
/* Structure to contain information about address of a service provider. */
struct addrinfo
{
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_canonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};