Как преобразовать IPv4-адрес в целое число в C#?

Я ищу функцию, которая преобразует стандартный адрес IPv4 в целое число. Бонусные баллы доступны для функции, которая будет делать обратное.

Решение должно быть в C#.

23 ответа

32-разрядные целые числа без знака являются адресами IPv4. Между тем, IPAddress.Address Свойство, хотя и устарело, представляет собой Int64, которое возвращает 32-разрядное значение без знака адреса IPv4 (уловка в том, что он находится в сетевом порядке байтов, поэтому вам необходимо поменять его местами).

Например, мой местный сайт 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));
    }
}

Чтобы преобразовать IPv4 в правильное целое число:

IPAddress address = IPAddress.Parse("255.255.255.255");
byte[] bytes = address.GetAddressBytes();
Array.Reverse(bytes); // flip big-endian(network order) to little-endian
uint intAddress = BitConverter.ToUInt32(bytes, 0);

И преобразовать обратно:

byte[] bytes = BitConverter.GetBytes(4294967295);
Array.Reverse(bytes); // flip little-endian to big-endian(network order)
string ipAddress = new IPAddress(bytes).ToString();

Объяснение:

IP-адреса в сетевом порядке (big-endian), в то время как intВ Windows они имеют прямой порядок байтов, поэтому для получения правильного значения необходимо преобразовать байты перед преобразованием.

Кроме того, даже для IPv4, int не может содержать адреса больше чем 127.255.255.255например, широковещательный адрес (255.255.255.255)так что используйте uint,

@ Барри Келли и @ Эндрю Хэйр, на самом деле, я не думаю, что умножение - самый ясный способ сделать это (хотя и правильно).

"Форматированный" IP-адрес Int32 можно рассматривать как следующую структуру

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

Попробуйте это:

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

Я столкнулся с некоторыми проблемами с описанными решениями, когда сталкиваюсь с IP-адресами с очень большим значением. В результате байт [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 (long)
            (
            16777216 * (long)bytes[0] +
            65536 * (long)bytes[1] +
            256 * (long)bytes[2] +
            (long)bytes[3]
            )
            ;
    }
    else
        return 0;
}

Как никто не опубликовал код, который использует 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()));

Реверс функции Дэви Лэндмана

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

Собрал несколько из приведенных выше ответов в метод расширения, который обрабатывает Endianness машины и обрабатывает адреса IPv4, которые были сопоставлены с IPv6.

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

Мой вопрос был закрыт, я понятия не имею, почему. Принятый ответ здесь не совпадает с тем, что мне нужно.

Это дает мне правильное целочисленное значение для 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;
    }
}

С 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();
}
    public static Int32 getLongIPAddress(string ipAddress)
    {
        return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
    }

Приведенный выше пример будет моим путем. Единственное, что вам может понадобиться - это конвертировать в UInt32 для отображения или строковых целей, включая использование его в качестве длинного адреса в строковой форме.

Что и требуется при использовании функции IPAddress.Parse(String). Вздох.

Умножьте все части номера IP на степени 256 (256x256x256, 256x256, 256 и 1. Например:

IPv4-адрес: 127.0.0.132-битное число: = (127x256^3) + (0x256^2) + (0x256^1) + 1= 2130706433

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

Если вас заинтересовала функция, а не просто ответ, вот как это делается:

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.

var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);

Предполагая, что у вас есть 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));

Я думаю, что это неправильно: "65536" ==> 0.0.255.255"Должно быть: "65535" ==> 0.0.255.255" или "65536" ==> 0.1.0.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

@ Дэви Лэдман, ваше решение со сдвигом корректно, но только для ip, начинающегося с числа, меньшего или равного 99, первый октет должен быть наложен на длинный

В любом случае конвертировать обратно с типом long довольно сложно, потому что хранить 64 бита (не 32 для Ip) и заполнять 4 байта нулями

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

static string ToAddr(uint address)
{
    return new IPAddress(address).ToString();
}

Наслаждайтесь!

Massimo

Вот решение, которое я разработал сегодня (должен был в первую очередь гуглить!):

    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, лямбду и некоторые расширения для дженериков, поэтому, хотя он дает тот же результат, он использует некоторые новые возможности языка, и вы можете сделать это в трех строках кода.

У меня есть объяснение в моем блоге, если вам интересно.

ура, -jc

Я использую это:

      public static uint IpToUInt32(string ip)
{
    if (!IPAddress.TryParse(ip, out IPAddress address)) return 0;
    return BitConverter.ToUInt32(address.GetAddressBytes(), 0);
}

public static string UInt32ToIp(uint address)
{
    return new IPAddress(address).ToString();
}

Я заметил, что System.Net.IPAddress имеет свойство Address (System.Int64) и конструктор, которые также принимают тип данных Int64. Таким образом, вы можете использовать это для преобразования IP-адреса в / из числового (хотя не Int32, но Int64) формата.

Взгляните на некоторые из сумасшедших примеров анализа в.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

Другие вопросы по тегам