Golang 网络编程丝绸之路 – TCP/UDP 地址解析
2010 年 4 月 1 日
TL;DR在使用 Golang 编写 TCP/UDP socket 的时候,第一步做的就是地址解析。
- func ResolveTCPAddr(network, address string) (*TCPAddr, error)
-
func ResolveUDPAddr(network, address string) (*UDPAddr, error)
下面是对这两个函数的源码分析。
ResolveTCPAddr
该函数返回的地址包含的信息如下:
// src/net/tcpsock.go type TCPAddr struct { IP IP Port int Zone string // IPv6 scoped addressing zone }
TCPAddr里, IP
既可以是 IPv4 地址,也可以是 IPv6 地址。 Port
就是端口了。 Zone
是 IPv6 本地地址所在的区域。
从返回结果看该函数的参数, network
指 address
的网络类型; address
指要解析的地址,会从中解析出我们想要的 IP
, Port
和 Zone
。
源码分析
// src/net/ipsock.go func ResolveTCPAddr(network, address string) (*TCPAddr, error) { // 检查 `network` 的值 switch network { case "tcp", "tcp4", "tcp6": case "": // a hint wildcard for Go 1.0 undocumented behavior network = "tcp" default: return nil, UnknownNetworkError(network) } // 使用默认解析器对 `address` 进行解析 addrs, err := DefaultResolver.internetAddrList(context.Background(), network, address) if err != nil { return nil, err } // 根据 `network` 和 `address` 返回一个地址 return addrs.forResolve(network, address).(*TCPAddr), nil }
从源码中可以看出,参数 network
只能是如下四个值,否则会得到一个错误。
默认解析器解析地址后返回一个地址列表 addrs
,该地址列表既包含了 IPv4 地址,也包含了 IPv6 地址。
-
“”: 将
network
置为 “tcp”,这是因为在使用默认解析器对address
进行解析时根据network
返回 TCP 地址*TCPAddr
。 -
“tcp”: 若
address
是 IPv6 地址,则该函数返回addrs
中的第一个IP
是 IPv6 的地址,否则返回addrs
中的第一个IP
是 IPv4 的地址。 -
“tcp4”: 该函数返回
addrs
中的第一个IP
是 IPv4 的地址。 -
“tcp6”: 该函数返回
addrs
中的第一个IP
是 IPv6 的地址。
addrs.forResolve
相关源码如下:
// src/net/ipsock.go // An addrList represents a list of network endpoint addresses. type addrList []Addr // isIPv4 reports whether addr contains an IPv4 address. func isIPv4(addr Addr) bool { switch addr := addr.(type) { case *TCPAddr: return addr.IP.To4() != nil ... } return false } // isNotIPv4 reports whether addr does not contain an IPv4 address. func isNotIPv4(addr Addr) bool { return !isIPv4(addr) } // forResolve returns the most appropriate address in address for // a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr. // IPv4 is preferred, unless addr contains an IPv6 literal. func (addrs addrList) forResolve(network, addr string) Addr { var want6 bool switch network { ... case "tcp", "udp": // IPv6 literal. (addr contains a port, so look for '[') want6 = count(addr, '[') > 0 } if want6 { return addrs.first(isNotIPv4) } return addrs.first(isIPv4) } // first returns the first address which satisfies strategy, or if // none do, then the first address of any kind. func (addrs addrList) first(strategy func(Addr) bool) Addr { for _, addr := range addrs { if strategy(addr) { return addr } } return addrs[0] }
ResolveUDPAddr
解析过程跟 ResolveTCPAddr
的一样,不过得到的是 *UDPAddr
。
UDPAddr
包含的信息如下:
// src/net/udpsock.go type UDPAddr struct { IP IP Port int Zone string // IPv6 scoped addressing zone }
源码分析
// src/net/udpsock.go func ResolveUDPAddr(network, address string) (*UDPAddr, error) { // 检查 `network` 的值 switch network { case "udp", "udp4", "udp6": case "": // a hint wildcard for Go 1.0 undocumented behavior network = "udp" default: return nil, UnknownNetworkError(network) } // 使用默认解析器对 `address` 进行解析 addrs, err := DefaultResolver.internetAddrList(context.Background(), network, address) if err != nil { return nil, err } // 根据 `network` 和 `address` 返回一个地址 return addrs.forResolve(network, address).(*UDPAddr), nil }