【问题标题】:Formatting IPv6 as an int in C# and storing it in SQL Server在 C# 中将 IPv6 格式化为 int 并将其存储在 SQL Server 中
【发布时间】:2010-10-20 22:42:21
【问题描述】:

IPv4 下,我一直在将IP 地址的字符串表示形式解析为Int32,并将它们作为INT 存储在SQL Server 中。

现在,对于IPv6,我正在尝试找出是否有标准或可接受的方法来使用C#IPv6 的字符串表示解析为两个Int64

人们如何将这些值存储在SQL Server 中 - 作为BIGINT 的两个字段?

【问题讨论】:

    标签: c# sql-server algorithm ipv6


    【解决方案1】:

    最简单的方法是让框架为您执行此操作。使用IPAddress.Parse解析地址,然后IPAddress.GetAddressBytes得到“数字”为byte[]。

    最后,将数组分成前8字节和后8字节,转换成两个Int64,例如:通过在字节数组上创建MemoryStream,然后通过BinaryReader 读取。

    这避免了需要了解 IPv6 地址的所有可用快捷表示。

    【讨论】:

    • 为什么是 2 个 int64 而不是 binary(128) 字段?
    • @Henry:这就是 Q 指定的内容。
    【解决方案2】:
    function encode_ip ($ip)
    {
        return bin2hex(inet_pton($ip));
    }
    
    function decode_ip($ip)
    {
        function hex2bin($temp) {
           $data="";
           for ($i=0; $i < strlen($temp); $i+=2) $data.=chr(hexdec(substr($temp,$i,2)));
           return $data;
        }
        return inet_ntop(hex2bin($ip));
    }
    

    -- max len row db
    echo strlen(inet_pton('2001:db8:85a3::8a2e:370:7334'));
    
    -- db row info
    ip varchar(16)
    
    -- sql binary save and read
    save base
    $bin_ip='0x'.bin2hex(inet_pton($data['ip_address']));
    
    -- db read
    select ip_address from users;
    
    -- encode binary from db
    echo inet_ntop($row['ip_address']);
    

    【讨论】:

    • 这是 PHP,OP 状态为 C#
    【解决方案3】:

    如果您使用的是 SQL Server 2005,则可以使用 uniqueidentifier 类型。此类型存储 16 个字节,非常适合 IPv6 ip 地址。您可以使用构造函数和ToByteArrayIPAddressGuid 之间进行转换。

    【讨论】:

    • uniqueidentifier 的问题是您不能进行范围搜索。 IE。您无法搜索包含一个特定 IP 的 IP 范围。我们使用包含 3.000.000 行数据的表格进行了尝试。而不是产生 3 行结果,我们得到 ~73.000
    【解决方案4】:

    我使用以下方法将一个 IP 地址转换为两个UInt64s (C# 3.0)。

    /// <summary>
    /// Converts an IP address to its UInt64[2] equivalent.
    /// For an IPv4 address, the first element will be 0,
    /// and the second will be a UInt32 representation of the four bytes.
    /// For an IPv6 address, the first element will be a UInt64
    /// representation of the first eight bytes, and the second will be the
    /// last eight bytes.
    /// </summary>
    /// <param name="ipAddress">The IP address to convert.</param>
    /// <returns></returns>
    private static ulong[] ConvertIPAddressToUInt64Array(string ipAddress)
    {
        byte[] addrBytes = System.Net.IPAddress.Parse(ipAddress).GetAddressBytes();
        if (System.BitConverter.IsLittleEndian)
        {
            //little-endian machines store multi-byte integers with the
            //least significant byte first. this is a problem, as integer
            //values are sent over the network in big-endian mode. reversing
            //the order of the bytes is a quick way to get the BitConverter
            //methods to convert the byte arrays in big-endian mode.
            System.Collections.Generic.List<byte> byteList = new System.Collections.Generic.List<byte>(addrBytes);
            byteList.Reverse();
            addrBytes = byteList.ToArray();
        }
        ulong[] addrWords = new ulong[2];
        if (addrBytes.Length > 8)
        {
            addrWords[0] = System.BitConverter.ToUInt64(addrBytes, 8);
            addrWords[1] = System.BitConverter.ToUInt64(addrBytes, 0);
        }
        else
        {
            addrWords[0] = 0;
            addrWords[1] = System.BitConverter.ToUInt32(addrBytes, 0);
        }
        return addrWords;
    }
    

    确保将UInt64s 转换为Int64s,然后再将它们放入数据库,否则您将获得ArgumentException。当您取回您的值时,您可以将它们转换回 UInt64 以获取无符号值。

    我不需要做相反的事情(即将UInt64[2] 转换为 IP 字符串),所以我从来没有为它构建方法。

    【讨论】:

    • 很少有你想要存储 255.255.255.255 或 FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF 的情况。
    • -1:Int32 可以很好地存储 IPv4 地址。您正在盲目地盯着整数的最大值,但这无关紧要。 32 位就是 32 位,它们显然足以表示 IPv4 地址的四个字节。
    • 我的错。我认为在尝试将 UInt32.MaxValue 转换为 Int32 时,CLR 会抛出溢出错误而不是“绕过拐角处”。我已经编辑了我的答案以反映这一点。
    • @Guffa:是的,但是 IPv6 地址有 128 位...... System.Net.IPAddress 必须同时使用两者。
    【解决方案5】:

    正如 IPv4 地址实际上是一个 32 位数字一样,IPv6 地址实际上是一个 128 位数字。地址有不同的字符串表示形式,但实际地址是数字,而不是字符串。

    因此,您无需将 IP 地址转换为数字,而是将地址的字符串表示形式解析为实际地址。

    即使是decimal 也不能容纳 128 位数字,因此剩下三个明显的替代方案:

    • 将数值存储为两个bigint 字段
    • 将地址的字符串表示形式存储在varchar 字段中
    • 将数值存储在一个 16 字节的 binary 字段中

    这两种方法都不如将 IPv4 地址存储在 int 中那样方便,因此您必须考虑它们对地址的限制。

    【讨论】:

    • @Bill:你错了。没有任何内容表明您必须将 IPv4 地址的字节存储在无符号值中。 Int32 和 UInt32 一样有 32 位,因此它可以很好地存储四个字节。
    • 幸好@RBarryYoung 没有找到这个线程;-) stackoverflow.com/a/1385701/215068 IPv4 是 4 字节二进制,而不是“真正的 32 位数字”。四个三位数的八位字节只是一个方便的符号
    • @EBarr:不,版本 4 中的 IP 号一个 32 位的数字,它不是一个 4 字节的数字。看一个IP包的描述,地址是32位的单个单元,没有分成4个8位的值。
    • 对不起,如果我的幽默不是很清楚,我对 2 个帖子之间的矛盾轻笑。对我来说,4 字节或 32 位似乎是同一枚硬币的两面。与 int 或 binary 相同的歧义存在于其他地方:RFC 719 p.12 声明“源地址:32 位”,关于此事的其他内容不多。 Core Internet Protocols p.408 将其描述为“实际上是一个 32 位二进制数”,wiki 说“32 位(四字节)地址”。 IP 寻址基础,第 22 页说“IP 地址是在长度为 32 位的数学空间内定义的。” 4 字节还是 32 位?是的,请!
    • @EBarr 虽然 IP 地址是 32 位数字(或 4 个字节),但它并不总是 int(由于机器字节序),这就是为什么建议将其存储为字节。例如,如果您要将 IP 192.168.1.1 转换为 x86/x64 机器上的 int,则它的值将是 0xC0A80101。如果您将该整数写入文件(或将其推送到网络上),它将以 0x0101A8C0 的顺序与位一起存储。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 2023-03-02
    • 1970-01-01
    • 2015-07-05
    • 2015-12-29
    相关资源
    最近更新 更多