【问题标题】:How to convert an IPv4 address into a integer in C#?如何在 C# 中将 IPv4 地址转换为整数?
【发布时间】:2010-10-02 11:34:52
【问题描述】:

我正在寻找将标准 IPv4 地址转换为整数的函数。可用于执行相反操作的功能的奖励积分。

解决方案应该在 C# 中。

【问题讨论】:

  • 警告:从接受的答案产生的整数将是错误,因为 IP 地址按网络顺序(大端),而 @ 987654322@s 在大多数系统上都是 little-endian。因此,您必须在转换之前反转字节。请参阅my answer 以获得正确的转换。此外,即使对于 IPv4,int 也无法保存大于 127.255.255.255 的地址,例如广播地址,因此请使用 uint

标签: c# integer ip ipv4


【解决方案1】:

32 位无符号整数 IPv4 地址。同时,IPAddress.Address 属性虽然已弃用,但它是一个 Int64,它返回 IPv4 地址的无符号 32 位值(问题是,它按网络字节顺序排列,因此您需要交换它)。

例如,我的本地 google.com 位于 64.233.187.99。这相当于:

64*2^24 + 233*2^16 + 187*2^8 + 99
= 1089059683

确实,http://1089059683/ 可以正常工作(至少在 Windows 中,已通过 IE、Firefox 和 Chrome 进行了测试;但在 iPhone 上不可用)。

这是一个显示两种转换的测试程序,包括网络/主机字节交换:

using System;
using System.Net;

class App
{
    static long ToInt(string addr)
    {
        // careful of sign extension: convert to uint first;
        // unsigned NetworkToHostOrder ought to be provided.
        return (long) (uint) IPAddress.NetworkToHostOrder(
             (int) IPAddress.Parse(addr).Address);
    }

    static string ToAddr(long address)
    {
        return IPAddress.Parse(address.ToString()).ToString();
        // This also works:
        // return new IPAddress((uint) IPAddress.HostToNetworkOrder(
        //    (int) address)).ToString();
    }

    static void Main()
    {
        Console.WriteLine(ToInt("64.233.187.99"));
        Console.WriteLine(ToAddr(1089059683));
    }
}

【讨论】:

  • 在 Ubuntu Linux 10.04 上的 Opera 11 中得到确认:它将 int 转换回熟悉的 w.x.y.z 形式,并且可以正常工作。
  • IPAddress.Address 从 .Net 4.0 开始已过时
  • @ErikPhilips 如果您只使用 IPv4,这有关系吗?
  • 这是一个架构决策。它有利有弊,而 cmets 不是讨论该特定问题的最佳场所。
  • 您可以使用 BitConverter.ToUInt32(IPAddress.Parse(value).GetAddressBytes().Reverse().ToArray() 来修复 IPAddress.Address 已过时
【解决方案2】:

这是从 IPv4 转换为 正确 整数并返回的一对方法:

public static uint ConvertFromIpAddressToInteger(string ipAddress)
{
    var address = IPAddress.Parse(ipAddress);
    byte[] bytes = address.GetAddressBytes();

    // flip big-endian(network order) to little-endian
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(bytes);
    }

    return BitConverter.ToUInt32(bytes, 0);
}

public static string ConvertFromIntegerToIpAddress(uint ipAddress)
{
    byte[] bytes = BitConverter.GetBytes(ipAddress);

    // flip little-endian to big-endian(network order)
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(bytes);
    }

    return new IPAddress(bytes).ToString();
}

示例

ConvertFromIpAddressToInteger("255.255.255.254"); // 4294967294
ConvertFromIntegerToIpAddress(4294967294); // 255.255.255.254

说明

IP 地址按网络顺序(大端),而ints 在 Windows 上是小端,因此要获得正确的值,您必须在小端系统上转换之前反转字节。

此外,即使对于 IPv4int 也不能保存大于 127.255.255.255 的地址,例如广播地址(255.255.255.255),所以使用uint

【讨论】:

  • 这实际上在使用 .NET 的 Windows 上似乎并没有什么不同——不确定 Mono。见dotnetfiddle.net/cBIOj2
  • @Jesse 它恰好不会对您输入的1.1.1.1 产生影响,因为它的字节数组是回文的。尝试使用非回文,例如 127.0.0.1192.168.1.1
  • 我看到了这个问题。重复的数字,例如 1.1.1.12.2.2.2123.123.123.123 总是产生相同的结果。对于后代,请参阅更新的小提琴:dotnetfiddle.net/aR6fhc
  • 请注意,我必须使用 System.Net.IPAddress 才能使其正常工作。效果很好!
  • @Jesse 这不仅适用于重复数字,还适用于所有palindromic IP 地址。所以2.1.1.2 是一样的。
【解决方案3】:

@Barry Kelly 和@Andrew Hare,实际上,我认为乘法并不是最明确的方法(完全正确)。

一个Int32“格式化”的IP地址可以看成如下结构

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
struct IPv4Address
{
   public Byte A;
   public Byte B;
   public Byte C;
   public Byte D;
} 
// to actually cast it from or to an int32 I think you 
// need to reverse the fields due to little endian

所以要转换 ip 地址 64.233.187.99 你可以这样做:

(64  = 0x40) << 24 == 0x40000000
(233 = 0xE9) << 16 == 0x00E90000
(187 = 0xBB) << 8  == 0x0000BB00
(99  = 0x63)       == 0x00000063
                      ---------- =|
                      0x40E9BB63

因此您可以使用 + 将它们相加,或者您可以二进制或一起使用。导致 0x40E9BB63 即 1089059683。(在我看来,以十六进制查看更容易看到字节)

所以你可以把函数写成:

int ipToInt(int first, int second, 
    int third, int fourth)
{
    return (first << 24) | (second << 16) | (third << 8) | (fourth);
}

【讨论】:

  • 我相信 ip 的指定方式是专门允许这种行为的......在大多数微处理器上,位移比 muls 和 add 更有效。
  • @Ape-in​​ago 乘以 2 的恒定幂通常会优化为位移,fwiw。
  • 您可以使用LayoutKind.ExplicitFieldOffset 来反转字节的存储顺序,而不是移位。当然,这只适用于小端架构。 Example on github.
  • 必须指出 int 是有符号的,所以如果你将 192 移动 24 位,你会得到负整数,所以这个代码会因为高八位字节的高位在第一位而被破坏。
【解决方案4】:

试试这个:

private int IpToInt32(string ipAddress)
{
   return BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes().Reverse().ToArray(), 0);
}

private string Int32ToIp(int ipAddress)
{
   return new IPAddress(BitConverter.GetBytes(ipAddress).Reverse().ToArray()).ToString();
}

【讨论】:

  • Reverse() 返回 void,因此您不能在其上调用 ToArray()(供未来的读者使用)。相反,为反转的字节分配一个值,然后您可以调用 ToArray()。
  • Reverse()IEnumerable 的扩展方法。上面的代码完全没问题。
  • 此方法对某些 IP(例如 140.117.0.0)产生负数
  • 我不介意负数,但它不适用于 IPV6 地址,例如fe80::202:b3ff:fe1e:8329
【解决方案5】:

由于没有人发布使用 BitConverter 并实际检查字节序的代码,因此如下:

byte[] ip = address.Split('.').Select(s => Byte.Parse(s)).ToArray();
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
int num = BitConverter.ToInt32(ip, 0);

然后返回:

byte[] ip = BitConverter.GetBytes(num);
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
string address = String.Join(".", ip.Select(n => n.ToString()));

【讨论】:

  • 你需要使用一个uint,但这是这里最正确的答案。
  • @RubberDuck:如果您想将 32 位数据作为无符号数,您只需要使用 uintint 就能够保存相同的信息。如果您想将其存储在数据库中,int 更适合,您需要 bigint 才能将其存储为未签名的形式。
  • uint 是 IMO 的更好表示。带符号的 int 需要一点符号,因此您会丢失范围最顶端的地址。是的,它可以保存相同的数据,但它会输出为负数,如果在地址栏中键入它不是有效的 IP。
  • @RubberDuck:uint 的形式你也不能在地址栏中输入它,你首先必须将它转换成文本形式才能做到这一点。仅仅因为使用将 int 转换为文本的最简单形式不会产生有效的 IP 地址,这并不是不使用它的好理由。
  • @RubberDuck:那不是uint,那是uint 的文本表示。
【解决方案6】:

在面对具有非常大值的 IP 地址时,我在所描述的解决方案中遇到了一些问题。 结果是,byte[0] * 16777216 的东西会溢出并变成负 int 值。 为我解决的问题是一个简单的类型转换操作。

public static long ConvertIPToLong(string ipAddress)
{
    System.Net.IPAddress ip;

    if (System.Net.IPAddress.TryParse(ipAddress, out ip))
    {
        byte[] bytes = ip.GetAddressBytes();

        return
            16777216L * bytes[0] +
            65536 * bytes[1] +
            256 * bytes[2] +
            bytes[3]
            ;
    }
    else
        return 0;
}

【讨论】:

  • 在我看来是最好的解决方案,但是我会更改 1 行,我会做 byte[] bytes=ip.MapToIPv4().GetAddressBytes();
【解决方案7】:

戴维兰德曼函数的逆向

string IntToIp(int d)
{
  int v1 = d & 0xff;
  int v2 = (d >> 8) & 0xff;
  int v3 = (d >> 16) & 0xff;
  int v4 = (d >> 24);
  return v4 + "." + v3 + "." + v2 + "." + v1;
}

【讨论】:

  • 您能详细解释一下吗?
【解决方案8】:

使用正确的小端格式的 UInt32,这里有两个简单的转换函数:

public uint GetIpAsUInt32(string ipString)
{
    IPAddress address = IPAddress.Parse(ipString);

    byte[] ipBytes = address.GetAddressBytes();

    Array.Reverse(ipBytes);

    return BitConverter.ToUInt32(ipBytes, 0);
}

public string GetIpAsString(uint ipVal)
{
    byte[] ipBytes = BitConverter.GetBytes(ipVal);

    Array.Reverse(ipBytes);

    return new IPAddress(ipBytes).ToString();
}

【讨论】:

    【解决方案9】:

    我的问题已结束,我不知道为什么。这里接受的答案与我需要的不同。

    这给了我正确的 IP 整数值..

    public double IPAddressToNumber(string IPaddress)
    {
        int i;
        string [] arrDec;
        double num = 0;
        if (IPaddress == "")
        {
            return 0;
        }
        else
        {
            arrDec = IPaddress.Split('.');
            for(i = arrDec.Length - 1; i >= 0 ; i = i -1)
                {
                    num += ((int.Parse(arrDec[i])%256) * Math.Pow(256 ,(3 - i )));
                }
            return num;
        }
    }
    

    【讨论】:

    • 只要你可以双向进行转换,我不明白为什么只要输出数字一致就必须正确。
    • 取决于你想用这个数字做什么。您不能使用其他转换来执行 >= 和
    【解决方案10】:

    将上述几个答案组合成一个扩展方法,该方法处理机器的字节顺序并处理映射到 IPv6 的 IPv4 地址。

    public static class IPAddressExtensions
    {
        /// <summary>
        /// Converts IPv4 and IPv4 mapped to IPv6 addresses to an unsigned integer.
        /// </summary>
        /// <param name="address">The address to conver</param>
        /// <returns>An unsigned integer that represents an IPv4 address.</returns>
        public static uint ToUint(this IPAddress address)
        {
            if (address.AddressFamily == AddressFamily.InterNetwork || address.IsIPv4MappedToIPv6)
            {
                var bytes = address.GetAddressBytes();
                if (BitConverter.IsLittleEndian)
                    Array.Reverse(bytes);
    
                return BitConverter.ToUInt32(bytes, 0);
            }
            throw new ArgumentOutOfRangeException("address", "Address must be IPv4 or IPv4 mapped to IPv6");
        }
    }
    

    单元测试:

    [TestClass]
    public class IPAddressExtensionsTests
    {
        [TestMethod]
        public void SimpleIp1()
        {
            var ip = IPAddress.Parse("0.0.0.15");
            uint expected = GetExpected(0, 0, 0, 15);
            Assert.AreEqual(expected, ip.ToUint());
        }
        [TestMethod]
        public void SimpleIp2()
        {
            var ip = IPAddress.Parse("0.0.1.15");
            uint expected = GetExpected(0, 0, 1, 15);
            Assert.AreEqual(expected, ip.ToUint());
        }
        [TestMethod]
        public void SimpleIpSix1()
        {
            var ip = IPAddress.Parse("0.0.0.15").MapToIPv6();
            uint expected = GetExpected(0, 0, 0, 15);
            Assert.AreEqual(expected, ip.ToUint());
        }
        [TestMethod]
        public void SimpleIpSix2()
        {
            var ip = IPAddress.Parse("0.0.1.15").MapToIPv6();
            uint expected = GetExpected(0, 0, 1, 15);
            Assert.AreEqual(expected, ip.ToUint());
        }
        [TestMethod]
        public void HighBits()
        {
            var ip = IPAddress.Parse("200.12.1.15").MapToIPv6();
            uint expected = GetExpected(200, 12, 1, 15);
            Assert.AreEqual(expected, ip.ToUint());
        }
        uint GetExpected(uint a, uint b, uint c, uint d)
        {
            return
                (a * 256u * 256u * 256u) +
                (b * 256u * 256u) +
                (c * 256u) +
                (d);
        }
    }
    

    【讨论】:

      【解决方案11】:
          public static Int32 getLongIPAddress(string ipAddress)
          {
              return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
          }
      

      上面的例子就是我要走的路。你唯一需要做的就是转换为 UInt32 用于显示目的,或字符串目的,包括将其用作字符串形式的长地址。

      这是使用 IPAddress.Parse(String) 函数时需要的。叹息。

      【讨论】:

        【解决方案12】:

        如果您对该功能感兴趣,这里的答案不仅仅是它是如何完成的:

        int ipToInt(int first, int second, 
            int third, int fourth)
        {
            return Convert.ToInt32((first * Math.Pow(256, 3))
                + (second * Math.Pow(256, 2)) + (third * 256) + fourth);
        }
        

        firstfourth 是 IPv4 地址的分段。

        【讨论】:

        • 我认为如果你使用 shift 而不是 Math.Pow 会更清楚。
        • 如果 first > 127,这将引发溢出异常。戴维兰德曼的回答是最好的方法。
        • int32 已签名,因此它将返回负值 > 127 用于第一个八位字节
        【解决方案13】:
        public bool TryParseIPv4Address(string value, out uint result)
        {
            IPAddress ipAddress;
        
            if (!IPAddress.TryParse(value, out ipAddress) ||
                (ipAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork))
            {
                result = 0;
                return false;
            }
        
            result = BitConverter.ToUInt32(ipAddress.GetAddressBytes().Reverse().ToArray(), 0);
            return true;
        }
        

        【讨论】:

          【解决方案14】:

          将 IP 编号的所有部分乘以 256 的幂(256x256x256、256x256、256 和 1。例如:

          IPv4 地址:127.0.0.1 32位数: = (127x256^3) + (0x256^2) + (0x256^1) + 1 = 2130706433

          【讨论】:

          • 这是最简单优雅的解决方案,恕我直言。不过,您可以将它放在 C++ 代码中,因为这是操作所要求的 :)
          【解决方案15】:

          这是我今天制定的解决方案(应该先用谷歌搜索!):

              private static string IpToDecimal2(string ipAddress)
              {
                  // need a shift counter
                  int shift = 3;
          
                  // loop through the octets and compute the decimal version
                  var octets = ipAddress.Split('.').Select(p => long.Parse(p));
                  return octets.Aggregate(0L, (total, octet) => (total + (octet << (shift-- * 8)))).ToString();
              }
          

          我正在使用 LINQ、lambda 和一些泛型扩展,因此虽然它产生了相同的结果,但它使用了一些新的语言特性,你可以用三行代码来完成。

          如果你有兴趣,我的博客上有解释。

          干杯, -jc

          【讨论】:

            【解决方案16】:

            我认为这是错误的:"65536" ==> 0.0.255.255" 应该是:"65535" ==> 0.0.255.255" 或 "65536" ==> 0.1.0.0"

            【讨论】:

              【解决方案17】:

              @Davy Ladman 您的 shift 解决方案是正确的,但仅适用于以小于或等于 99 的数字开头的 ip,事实上第一个八位字节必须转换为 long。

              无论如何用 long 类型转换回来是相当困难的,因为存储 64 位(对于 Ip 不是 32 位)并用零填充 4 个字节

              static uint ToInt(string addr)
              {
                 return BitConverter.ToUInt32(IPAddress.Parse(addr).GetAddressBytes(), 0);
              }
              
              static string ToAddr(uint address)
              {
                  return new IPAddress(address).ToString();
              }
              

              享受吧!

              马西莫

              【讨论】:

                【解决方案18】:

                假设您有一个字符串格式的 IP 地址(例如 254.254.254.254)

                string[] vals = inVal.Split('.');
                uint output = 0;
                for (byte i = 0; i < vals.Length; i++) output += (uint)(byte.Parse(vals[i]) << 8 * (vals.GetUpperBound(0) - i));
                

                【讨论】:

                  【解决方案19】:
                  var ipAddress = "10.101.5.56";
                  
                  var longAddress = long.Parse(string.Join("", ipAddress.Split('.').Select(x => x.PadLeft(3, '0'))));
                  
                  Console.WriteLine(longAddress);
                  

                  输出:10101005056

                  【讨论】:

                    【解决方案20】:
                    var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
                    long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
                    

                    【讨论】:

                    • GetAddressBytes 方法可能会以相反的顺序返回字节,具体取决于机器是字节序还是字节序,因此正确的语句可能适用于某些机器:long m_Address = (address[0]
                    【解决方案21】:

                    我注意到 System.Net.IPAddress 具有 Address 属性 (System.Int64) 和构造函数,它们也接受 Int64 数据类型。因此,您可以使用它来将 IP 地址转换为数字(虽然不是 Int32,而是 Int64)格式。

                    【讨论】:

                    • 这应该是一条评论,因为它没有回答问题!
                    【解决方案22】:

                    看看 .Net 的 IPAddress.Parse 中一些疯狂的解析示例: (MSDN)

                    “65536”==> 0.0.255.255
                    “20.2” ==> 20.0.0.2
                    “20.65535” ==> 20.0.255.255
                    “128.1.2” ==> 128.1.0.2

                    【讨论】:

                    • 这不是一个真正的答案。为什么不将其作为对热门答案之一的评论?
                    猜你喜欢
                    • 1970-01-01
                    • 2014-03-03
                    • 2011-02-16
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-12-06
                    • 1970-01-01
                    • 2018-01-23
                    • 1970-01-01
                    相关资源
                    最近更新 更多