1. 程式人生 > >Golang學習之net包介紹

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包的文件。