Как преобразовать 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