Golang學習之net包介紹
與大多數語言一樣,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.parseIPV4
undefined: net.parseIPV4
因為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包的文件。