如何在C#中将IPv4地址转换为整数?


99

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

解决方案应该在C#中。


15
警告:接受的答案产生的整数将是错误的,因为IP地址按网络顺序排列(大端),而int在大多数系统上,s均为小端。因此,您必须在转换之前反转字节。请参阅我的答案以获得正确的转换。此外,即使对于IPv4,一个int不能持有超过更大的地址127.255.255.255广播地址,所以使用uint
Saeb Amini 2012年

Answers:


137

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));
    }
}

2
在Ubuntu Linux 10.04的Opera 11中得到确认:它将int转换回熟悉的wxyz形式,并且可以正常工作。
Piskvor于

6
从.Net 4.0开始,IPAddress.Address已过时
Erik Philips

@ErikPhilips如果仅使用IPv4,这有关系吗?
2014年

这是一个架构决定。它有其优点和缺点,并且评论不是讨论该特定问题的最佳位置。
Erik Philips

1
您可以使用BitConverter.ToUInt32(IPAddress.Parse(value).GetAddressBytes()。Reverse()。ToArray()修复IPAddress.Address已过时
-Mhmd,

43

这是将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地址按网络顺序排列(big-endian),而ints在Windows上为little-endian,因此,要获取正确的值,必须先反转字节,然后再在little-endian系统上进行转换。

另外,即使对于IPv4int也无法容纳比127.255.255.255广播地址大的地址(255.255.255.255),因此请使用uint


在使用.NET的Windows上,这实际上似乎没有什么不同-不确定Mono。参见dotnetfiddle.net/cBIOj2
Jesse,

2
@Jesse它恰好对您的输入没有影响,1.1.1.1因为它的字节数组是回文的。尝试使用非回文疗法,例如127.0.0.1192.168.1.1
Saeb Amini

我看到了问题。重复的数字(例如1.1.1.12.2.2.2123.123.123.123始终会产生相同的结果。有关后代,请参见更新的提琴:dotnetfiddle.net/aR6fhc
Jesse

要指出的是,我必须使用它System.Net.IPAddress才能使它正常工作。很棒!
Shrout18年

1
@Jesse不仅是重复数字,还包括所有回文 IP地址。所以2.1.1.2也一样。
Saeb Amini

40

@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

因此您可以使用+将它们加起来,也可以将binairy或它们加在一起。结果为0x40E9BB63,即1089059683。(我认为以十六进制形式查看字节要容易得多)

因此,您可以将函数编写为:

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

1
我相信ip被认为是专门允许这种行为的方式……大多数微处理器上的移位比mul和add更为有效。
Ape-in​​ago

1
@ Ape-in​​ago乘以2的恒定幂的乘法通常被优化为移位fwiw。
巴里·凯利

您可以使用LayoutKind.ExplicitFieldOffset反转字节的存储顺序,而不是移位。当然,这仅适用于小端架构。github上的示例
RubberDuck

2
必须指出,该int符号是有符号的,因此,如果将192移位24位,您将得到负整数,因此该代码将被破坏,因为高位字节首先具有高位。
Mateusz

12

试试这个:

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()。
Erik Philips

1
Reverse()是的扩展方法IEnumerable。上面的代码完全可以。
2014年

3
此方法对某些IP(例如140.117.0.0)产生负数
lightxx

7

由于没有人发布使用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()));

2
您需要使用uint,但这是这里最正确的答案。
RubberDuck

1
@RubberDuck:uint如果要将32位数据作为无符号数字,int则仅需要使用an ,它能够保存相同的信息。如果要将其存储在数据库int中更合适,则需要a bigint能够以未签名的形式存储它。
Guffa

1
uint是IMO的更好表示。一个有符号的int需要一点符号,因此您会丢失该范围顶部的地址。是的,它可以保存相同的数据,但是将输出为负数,如果您在地址栏中输入该数字,则该IP地址无效。
RubberDuck

@RubberDuck:uint您也无法在地址栏中输入的形式,首先必须将其转换为文本形式。仅仅因为使用最简单的形式将int文本转换为文本不会产生有效的IP地址,对于不使用它不是一个很好的论据。
Guffa

@RubberDuck:不是uint,而是的文本表示形式uint
Guffa

7

当面对价值非常高的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();
Walter Vehoeven

4

戴维·兰德曼(Davy Landman)功能的反面

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;
}

你能解释一下吗?
Developerium

3

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

这为我提供了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;
    }
}

只要您能同时进行转换,我就看不出为什么输出数只要是一致的就必须正确。
GateKiller

取决于您要使用数字的用途。你不能用其他的转换做一个> =和<=查询找到一个IP ..
Coolcoder

2

使用正确的Little-Endian格式的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();
}

2

将以上几个答案组合到一个扩展方法中,该方法可以处理计算机的字节序并处理映射到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);
    }
}

1

如果您对该功能感兴趣,不仅可以找到答案,还可以找到它的实现方式:

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);
}

first通过fourth为IPv4地址的区段。


1
我想如果您使用shift而不是Math.Pow会更清楚。
Mehrdad Afshari

如果first> 127,则将引发溢出异常。Davy Landman的答案是做到这一点的最佳方法。
mhenry1384 2012年

int32是带符号的,因此它将为第一个八位字节返回负值> 127
Mateusz

1
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;
}

1
    public static Int32 getLongIPAddress(string ipAddress)
    {
        return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
    }

上面的示例就是我要走的路。您可能要做的就是将其转换为UInt32以便显示或字符串,包括将其用作字符串形式的长地址。

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


0

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

    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


0

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


0

@Davy Ladman您的移位解决方案是正确的,但仅对于以数字小于或等于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();
}

请享用!

马西莫


0

假设您具有字符串格式的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));

0
var ipAddress = "10.101.5.56";

var longAddress = long.Parse(string.Join("", ipAddress.Split('.').Select(x => x.PadLeft(3, '0'))));

Console.WriteLine(longAddress);

输出:10101005056


0
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] << 24 | address [1] << 16 | address [2] << 8 | address [3])&0x0FFFFFFFF
MiguelSlv '16

0

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


-1

看一下.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


这不是真正的答案。为什么不把它作为对最重要答案之一的评论?
DanM7
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.