Go語言的net/netip
包詳解:Addr
類型
大家好!今天我們深入探討Go語言的net/netip
包,重點放在Addr
類型。如果您一直在使用Go的網路程式碼,您可能已經遇到了舊的net.IP
類型。雖然它為我們服務良好,但它有一些缺點,使其不太適合現代網路程式碼。 net/netip
套件(在Go 1.18中引入)為我們提供了一種更強大、更有效率的處理IP位址的方法。
net/netip.Addr
? 在我們深入細節之前,讓我們先了解為什麼存在這種類型。傳統的net.IP
類型基本上是一個位元組切片([]byte
),這意味著:
==
運算子來比較新的Addr
類型解決了所有這些問題。它是一個值類型(內部結構體),不可變,並且始終表示有效的IP位址。不再需要防禦性程式設計!
Addr
讓我們看看建立和使用Addr
的基本方法:
<code class="language-go">package main import ( "fmt" "net/netip" ) func main() { // 从字符串创建Addr addr, err := netip.ParseAddr("192.168.1.1") if err != nil { panic(err) } // 如果你绝对确定输入 addr2 := netip.MustParseAddr("2001:db8::1") fmt.Printf("IPv4: %v\nIPv6: %v\n", addr, addr2) }</code>
ParseAddr
的一個優點是它非常嚴格。它不會接受奇怪的格式或無效的地址。例如:
<code class="language-go">// 这些将会失败 _, err1 := netip.ParseAddr("256.1.2.3") // 无效的IPv4八位字节 _, err2 := netip.ParseAddr("2001:db8::1::2") // 无效的IPv6(双冒号) _, err3 := netip.ParseAddr("192.168.1.1/24") // Addr不允许CIDR表示法</code>
Addr
方法讓我們探索您將與Addr
一起使用的關鍵方法。我將分享一些實際範例,說明每個方法在哪些情況下派上用場。
<code class="language-go">func checkAddressType(addr netip.Addr) { if addr.Is4() { fmt.Println("这是IPv4") // 你可以在这里安全地使用As4() bytes := addr.As4() fmt.Printf("作为字节:%v\n", bytes) } else if addr.Is6() { fmt.Println("这是IPv6") // 你可以在这里安全地使用As16() bytes := addr.As16() fmt.Printf("作为字节:%v\n", bytes) } }</code>
專業提示:當處理IPv4對映的IPv6位址(例如::ffff:192.0.2.1
)時,使用Is4In6()
來偵測它們。這在編寫與協議無關的程式碼時特別有用。
Addr
類型提供了幾種方法來對IP位址進行分類。這是一個全面的範例:
<code class="language-go">func classifyAddress(addr netip.Addr) { checks := []struct { name string fn func() bool }{ {"IsGlobalUnicast", addr.IsGlobalUnicast}, {"IsPrivate", addr.IsPrivate}, {"IsLoopback", addr.IsLoopback}, {"IsMulticast", addr.IsMulticast}, {"IsLinkLocalUnicast", addr.IsLinkLocalUnicast}, {"IsLinkLocalMulticast", addr.IsLinkLocalMulticast}, {"IsInterfaceLocalMulticast", addr.IsInterfaceLocalMulticast}, {"IsUnspecified", addr.IsUnspecified}, } for _, check := range checks { if check.fn() { fmt.Printf("地址是 %s\n", check.name) } } }</code>
實際範例:假設您正在編寫一項服務,該服務需要綁定到除環回介面之外的所有介面:
<code class="language-go">func getBindableAddresses(addrs []netip.Addr) []netip.Addr { var bindable []netip.Addr for _, addr := range addrs { if !addr.IsLoopback() && !addr.IsLinkLocalUnicast() { bindable = append(bindable, addr) } } return bindable }</code>
如果您使用的是IPv6,最終會遇到區域。它們主要與鏈路本地位址一起使用,以指定要使用的網路介面:
<code class="language-go">func handleZones() { // 创建一个带有区域的地址 addr := netip.MustParseAddr("fe80::1%eth0") // 获取区域 zone := addr.Zone() fmt.Printf("区域:%s\n", zone) // 比较带有区域的地址 addr1 := netip.MustParseAddr("fe80::1%eth0") addr2 := netip.MustParseAddr("fe80::1%eth1") // 由于区域不同,这些是不同的地址 fmt.Printf("相同的地址?%v\n", addr1 == addr2) // false // WithZone创建一个具有不同区域的新地址 addr3 := addr1.WithZone("eth2") fmt.Printf("新的区域:%s\n", addr3.Zone()) }</code>
讓我們在一個實際範例中將所有這些放在一起。這是一個簡單的IP過濾器,可用於Web服務:
<code class="language-go">type IPFilter struct { allowed []netip.Addr denied []netip.Addr } func NewIPFilter(allowed, denied []string) (*IPFilter, error) { f := &IPFilter{} // 解析允许的地址 for _, a := range allowed { addr, err := netip.ParseAddr(a) if err != nil { return nil, fmt.Errorf("无效的允许地址 %s: %w", a, err) } f.allowed = append(f.allowed, addr) } // 解析拒绝的地址 for _, d := range denied { addr, err := netip.ParseAddr(d) if err != nil { return nil, fmt.Errorf("无效的拒绝地址 %s: %w", d, err) } f.denied = append(f.denied, addr) } return f, nil } func (f *IPFilter) IsAllowed(ip string) bool { addr, err := netip.ParseAddr(ip) if err != nil { return false } // 首先检查拒绝列表 for _, denied := range f.denied { if addr == denied { return false } } // 如果没有指定允许的地址,则允许所有未被拒绝的地址 if len(f.allowed) == 0 { return true } // 检查允许列表 for _, allowed := range f.allowed { if addr == allowed { return true } } return false }</code>
使用方法範例:
<code class="language-go">func main() { filter, err := NewIPFilter( []string{"192.168.1.100", "10.0.0.1"}, []string{"192.168.1.50"}, ) if err != nil { panic(err) } tests := []string{ "192.168.1.100", // 允许 "192.168.1.50", // 拒绝 "192.168.1.200", // 不在任何列表中 } for _, ip := range tests { fmt.Printf("%s 允许?%v\n", ip, filter.IsAllowed(ip)) } }</code>
net/netip.Addr
的一大優點是其性能特性。由於它是一個值類型:
net.IP
不同,其中零值可能是有效的)net.IP
和netip.Addr
雖然可以在它們之間進行轉換,但為了保持一致性,盡量在整個程式碼庫中堅持使用netip.Addr
。 MustParseAddr
雖然在測試或初始化程式碼中很方便,但在處理使用者輸入的生產程式碼中,更喜歡ParseAddr
。 WithZone
)實際上都會回傳一個新的地址。 本文介紹了Addr
類型的基礎知識和一些高級用法,但net/netip
包中還有更多內容需要探索。在下一篇文章中,我們將研究AddrPort
,它將IP位址與連接埠號碼結合在一起——這對於網路程式設計非常有用。
在那之前,祝您編程愉快!如果您在專案中使用net/netip.Addr
有任何問題,請隨時與我們聯繫。
以上是了解 Go 的 net/netip Addr 類型:深入探討的詳細內容。更多資訊請關注PHP中文網其他相關文章!