Як перетворити IPv4 адресу в ціле число в C #?


99

Я шукаю функцію, яка перетворить стандартний IPv4-адресу в цілий. Доступні бонусні бали за функцію, яка буде робити навпаки.

Рішення повинно бути в C #.


15
ПОПЕРЕДЖЕННЯ : цілі числа, отримані в результаті прийнятої відповіді, будуть помилковими , оскільки IP-адреси впорядковані в мережевому порядку (big-endian), а ints в малосистемних системах у більшості систем. Тому перед перетворенням потрібно перевернути байти. Дивіться мою відповідь щодо правильних перетворень. Крім того, навіть для IPv4, intадреса не може містити більше, ніж 127.255.255.255, наприклад, широкомовна адреса, тому використовуйте a uint.
Саеб Аміні

Відповіді:


137

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

2
Підтверджено в Opera 11 на Ubuntu Linux 10.04: він перетворює int назад у звичну форму wxyz, і вона працює.
Пісквор вийшов з будівлі

6
IPAddress.Address застаріло станом на .Net 4.0.
Ерік Філіпс

@ErikPhilips Це має значення, якщо ви використовуєте лише IPv4?
Рей

Це архітектурне рішення. Є свої плюси і мінуси, а коментарі - не найкраще місце для обговорення конкретного питання.
Ерік Філіпс

1
ви можете використовувати BitConverter.ToUInt32 (IPAddress.Parse (значення) .GetAddressBytes (). Зворотний (). ToArray () для виправлення IPAddress.
Адреса

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, тож, щоб отримати правильне значення, необхідно перетворити байти перед перетворенням у систему з малою ендіанією.

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


Це насправді не має значення для Windows, використовуючи .NET - не впевнений у Mono. Дивіться dotnetfiddle.net/cBIOj2
Джессі

2
@Jesse, трапляється, не впливає на ваш внесок, 1.1.1.1оскільки його байтовий масив є паліндромним. Спробуйте з непаліндромними, такими як 127.0.0.1або 192.168.1.1.
Саеб Аміні

Я бачу питання. Повторні числа , такі як 1.1.1.1, 2.2.2.2, 123.123.123.123завжди дають той же результат. Для нащадків дивіться оновлену скрипку: dotnetfiddle.net/aR6fhc
Джессі

Хотілося б зауважити, що мені довелося скористатися System.Net.IPAddressдля роботи. Чудово працює!
Shrout1

1
@Jesse, це не для повторення чисел, а для всіх паліндромних IP-адрес. Так 2.1.1.2би було те саме.
Саеб Аміні

40

@Barry Kelly та @Andrew Hare, насправді, я не думаю, що множення є найбільш зрозумілим способом зробити це (все-таки правильно).

"Форматирована" 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

тож ви можете додати їх за допомогою + або ви можете створити файли або їх разом. У результаті виходить 0x40E9BB63, що становить 1089059683. (На мою думку, дивлячись у шістнадцятковій формі набагато простіше бачити байти)

Таким чином, ви можете записати функцію як:

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

1
Я вважаю, що ip-i були розроблені таким чином, щоб спеціально допустити таку поведінку ... зсуви бітів набагато ефективніші для більшості мікропроцесорів, ніж мульти та доповнення.
Ape-inago

1
@ Апе-інаго множення на постійні сили двох звичайно оптимізовано в бітові зсуви, fwiw.
Баррі Келлі

Замість бітового зсуву ви можете використовувати LayoutKind.Explicitта FieldOffsetзмінювати порядок, в якому зберігаються байти. Звичайно, це працює лише для маленької архітектури ендіан. Приклад на github .
RubberDuck

2
Потрібно зазначити, що intце підписано, тому якщо ви зміните 192 на 24 біти, ви отримаєте від'ємне ціле число, тому цей код розбитий на високий октет, що має високий біт на першому місці.
Матеуш

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()повертається недійсно, тому ви не можете його закликати ToArray()(для майбутніх читачів). Замість цього призначте значення оберненим байтам, тоді ви можете викликати ToArray ().
Ерік Філіпс

1
Reverse () - метод розширення IEnumerable. Наведений вище код абсолютно добре.
Мураха

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
Потрібно скористатися утінкою, але це найбільш правильна відповідь тут.
RubberDuck

1
@RubberDuck: Використовувати дані потрібно лише в тому випадку, uintякщо ви хочете, щоб 32 біти даних були як безпідписане число, а intздатне зберігати ту саму інформацію. Якщо ви хочете зберігати його в базі даних, а intкраще підходить, вам потрібно bigintмати змогу зберігати її у неподписаному вигляді.
Guffa

1
Унт - це краще представлення ІМО. Підписаний int потребує трохи знаку, тому ви втрачаєте адреси в самому верху діапазону. Так, він може містити ті самі дані, але він буде виводитися як негативне число, що не є дійсним IP-адресою, якщо ви введете його в адресному рядку.
RubberDuck

@RubberDuck: У формі uintви також не можете ввести його в адресному рядку, для цього спочатку потрібно перетворити його в текстову форму. Тільки тому, що використання найпростішої форми перетворення intтексту не створює працюючої IP-адреси, це не є гарним аргументом для її використання.
Guffa

@RubberDuck: Це не an uint, це текстове зображення uint.
Guffa

7

Я стикався з деякими проблемами з описаними рішеннями, коли стикався з IP адресами з дуже великим значенням. Результатом було б те, що байт [0] * 16777216 thingy переповниться і перетвориться в негативне ціле значення. для мене це виправлено - це проста форма операцій лиття типу.

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 рядок, я зробив би байт [] байт = ip.MapToIPv4 (). GetAddressBytes ();
Вальтер Веховен

4

Зворотна функція Деві Лендмена

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

З 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

Зібрано декілька вищезазначених відповідей на метод розширення, який обробляє ендіантність машини та обробляє адреси 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);
    }
}

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.
Мехрдад Афшарі

Це призведе до виключення переповнення, якщо спочатку це> 127. Відповідь Деві Лендмана є найкращим способом зробити це.
mhenry1384

int32 підписаний, тому він поверне від’ємне значення> 127 для першого октету
Матеуш

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

ось рішення, яке я розробив сьогодні (мав би вперше гугл!):

    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


0

Я думаю, що це неправильно: "65536" ==> 0.0.255.255 "Має бути:" 65535 "==> 0.0.255.255" або "65536" ==> 0.1.0.0 "


0

@Davy Ladman ваше рішення зі зрушенням є коректним, але тільки для ip, починаючи з числа менше або рівного 99, інфактний перший октект повинен бути доданий до довгого.

У будь-якому випадку перетворити назад з довгим типом досить складно, оскільки зберігати 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();
}

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

Массімо


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 = (адреса [0] << 24 | адреса [1] << 16 | адреса [2] << 8 | адреса [3]) & 0x0FFFFFFFF
MiguelSlv

0

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


-1

Ознайомтеся з деякими шаленими прикладами розбору в IPAddress.Parse: .Net: ( 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.