IP-netNet的IP部分不满足reflect.DeepEqual,但是等于

时间:2017-03-21 07:58:23

标签: go

我在单元测试中遇到意外行为,它使用reflect.DeepEqual来测试两个已解析结构的相等性。结构包含一片net.IPNet结构。测试失败,因为reflect.DeepEqual返回false。

在追踪此问题的同时,我发现了DeepEqual的以下意外行为。对我来说最吸引人的是它可以使用IPv6地址并使用战争解析的IP,而不是net.IPNet结构中的掩码。

任何人都可以向我解释:

  • 为什么这些IPv4地址不是DeepEqual,尽管它们的字节表示似乎是?
  • 为什么他们的DeepEqual用于IPv6地址?
  • 如何构建一个与ParseCIDR生成的关于DeepEqual的网络实例匹配?

示例程序,可以go run运行:

package main

import (
    "fmt"
    "net"
    "reflect"
)

func main() {
    aip, a, _ := net.ParseCIDR("135.104.0.0/32")
    //aip, a, _ := net.ParseCIDR("abcd:2345::/65")

    bip := net.IPv4(135, 104, 0, 0)
    //bip := net.ParseIP("abcd:2345::")


    // IPa: 135.104.0.0 3133352e3130342e302e30
    fmt.Printf("IPa: %s %x\n", a.IP, a.IP)

    // IPb: 135.104.0.0 3133352e3130342e302e30
    fmt.Printf("IPb: %s %x\n", bip, bip)

    fmt.Println("eq?:", a.IP.Equal(bip)) // true

    // I'd expect this to be true
    fmt.Println("deep eq?:", reflect.DeepEqual(a.IP, bip)) // false

    fmt.Println("deep eq w/o mask?:", reflect.DeepEqual(aip, bip)) // true
}

1 个答案:

答案 0 :(得分:1)

他们并不平等,因为他们不会遇到definition of deep equality

通过查看每个值的内部表示,正如我on this playground所做的那样,我们可以看到,虽然它们都是IPv4地址,但bip前面有一些额外的字节(可能是IPv6)代表......为什么,我不确定。

IPa: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x87, 0x68, 0x0, 0x0}
IPb: 135.104.0.0 3133352e3130342e302e30 net.IP net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x87, 0x68, 0x0, 0x0}

此外,使用DeepEqual比较一些基本的IP地址是一个非常糟糕的想法™,原因很简单,因为IP 不是深层结构。它们(出于平等目的)是简单的不透明值。所以直接比较不透明值。这将意味着从各种存储结构输出一些内容,但以通用格式输出它们并进行比较。比较字节表示中的直接IP(int32)通常是最有效的。如果您需要比较网络地址,网络掩码等,请以相同的方式进行。或者,比较字符串表示,如果这对您的工具更容易。

但核心是,IP地址只是一个32位整数(在IPv6的情况下是128位整数),其他属性是其他整数。检查整数上的深度等式是没有意义的。