首頁 > 後端開發 > Golang > 了解 Go 的 net/netip Addr 類型:深入探討

了解 Go 的 net/netip Addr 類型:深入探討

Susan Sarandon
發布: 2025-01-11 10:55:42
原創
941 人瀏覽過

Understanding Go

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一起使用的關鍵方法。我將分享一些實際範例,說明每個方法在哪些情況下派上用場。

這是IPv4還是IPv6?

<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作用域ID)

如果您使用的是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位址過濾器

讓我們在一個實際範例中將所有這些放在一起。這是一個簡單的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不同,其中零值可能是有效的)

一些常見的陷阱和技巧

  1. 不要隨意混合使用net.IPnetip.Addr 雖然可以在它們之間進行轉換,但為了保持一致性,盡量在整個程式碼庫中堅持使用netip.Addr
  2. 注意比較中的區域 除了區域之外,兩個相同的位址被認為是不同的位址。
  3. 謹慎使用MustParseAddr 雖然在測試或初始化程式碼中很方便,但在處理使用者輸入的生產程式碼中,更喜歡ParseAddr
  4. 記住它是不可變的 所有似乎修改地址的方法(例如WithZone)實際上都會回傳一個新的地址。

接下來是什麼?

本文介紹了Addr類型的基礎知識和一些高級用法,但net/netip包中還有更多內容需要探索。在下一篇文章中,我們將研究AddrPort,它將IP位址與連接埠號碼結合在一起——這對於網路程式設計非常有用。

在那之前,祝您編程愉快!如果您在專案中使用net/netip.Addr有任何問題,請隨時與我們聯繫。

以上是了解 Go 的 net/netip Addr 類型:深入探討的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板