32

Golang 网络编程丝绸之路 - TCP/UDP 地址解析

 4 years ago
source link: https://www.tuicool.com/articles/neq6v2n
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

TL;DR在使用 Golang 编写 TCP/UDP socket 的时候,第一步做的就是地址解析。

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 本地地址所在的区域。

从返回结果看该函数的参数, networkaddress 的网络类型; address 指要解析的地址,会从中解析出我们想要的 IP , PortZone

源码分析

// 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 地址。

  1. "": 将 network 置为 "tcp",这是因为在使用默认解析器对 address 进行解析时根据 network 返回 TCP 地址 *TCPAddr
  2. "tcp": 若 address 是 IPv6 地址,则该函数返回 addrs 中的第一个 IP 是 IPv6 的地址,否则返回 addrs 中的第一个 IP 是 IPv4 的地址。
  3. "tcp4": 该函数返回 addrs 中的第一个 IP 是 IPv4 的地址。
  4. "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
}

Reference


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK