Я ищу функцию, которая преобразует стандартный адрес IPv4 в целое число. Бонусные баллы доступны за функцию, которая будет делать обратное.
Решение должно быть на C #.
Я ищу функцию, которая преобразует стандартный адрес IPv4 в целое число. Бонусные баллы доступны за функцию, которая будет делать обратное.
Решение должно быть на C #.
Ответы:
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 в правильное целое число и обратно:
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-адреса расположены в сетевом порядке (с прямым порядком байтов), а int
s - с прямым порядком байтов в Windows, поэтому для получения правильного значения вы должны перевернуть байты перед преобразованием в системе с прямым порядком байтов.
Кроме того, даже для IPv4
, не int
может содержать адреса больше, чем 127.255.255.255
, например, широковещательный адрес (255.255.255.255)
, поэтому используйте uint
.
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
чтобы это работало. Прекрасно работает!
2.1.1.2
было бы тоже самое.
@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
чтобы вы могли сложить их, используя +, или вы могли бы binairy или их вместе. Результатом является 0x40E9BB63, который равен 1089059683. (На мой взгляд, глядя в шестнадцатеричном формате, намного легче увидеть байты)
Итак, вы можете написать функцию как:
int ipToInt(int first, int second,
int third, int fourth)
{
return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
LayoutKind.Explicit
и FieldOffset
для изменения порядка, в котором хранятся байты. Конечно, это работает только для архитектуры с прямым порядком байтов. Пример на гитхабе .
int
это подписано, поэтому, если вы сдвинете 192 на 24 бита, вы получите отрицательное целое число, поэтому этот код нарушается для старшего октета, имеющего высокий бит на первом месте.
Попробуйте эти:
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 ().
IEnumerable
. Код выше в порядке.
Поскольку никто не опубликовал код, который использует 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
если вы хотите, чтобы 32 бита данных были беззнаковым числом, int
он может хранить ту же информацию. Если вы хотите сохранить его в базе данных, что int
лучше подходит, вам понадобится, bigint
чтобы иметь возможность хранить его в неподписанной форме.
uint
вы также не можете ввести в адресной строке, вам сначала нужно преобразовать ее в текстовую форму, чтобы сделать это. Тот факт, что использование простейшей формы преобразования int
в текст не приводит к созданию рабочего IP-адреса, не является хорошим аргументом в пользу его отказа .
uint
, это текстовое представление uint
.
Я столкнулся с некоторыми проблемами с описанными решениями, когда сталкивался с 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
16777216L * bytes[0] +
65536 * bytes[1] +
256 * bytes[2] +
bytes[3]
;
}
else
return 0;
}
Обратная функция Дэви Лэндмана
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;
}
Мой вопрос был закрыт, я не знаю почему. Принятый здесь ответ отличается от того, что мне нужно.
Это дает мне правильное целочисленное значение для 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();
}
Собрал несколько из приведенных выше ответов в метод расширения, который обрабатывает порядок байтов машины и обрабатывает адреса 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);
}
}
Если вас интересовала функция, а не просто ответ, вот как это делается:
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.
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;
}
public static Int32 getLongIPAddress(string ipAddress)
{
return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
}
Приведенный выше пример - это то, что я использую ... Единственное, что вам, возможно, придется сделать, это преобразовать в UInt32 для целей отображения или строковых целей, включая использование его как длинного адреса в строковой форме.
Это то, что нужно при использовании функции IPAddress.Parse (String). Вздох.
вот решение, которое я разработал сегодня (сначала нужно было погуглить!):
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
Я считаю, что это неправильно: "65536" ==> 0.0.255.255 "Должно быть:" 65535 "==> 0.0.255.255" или "65536" ==> 0.1.0.0 "
@Davy Ladman ваше решение со сдвигом является коррентным, но только для 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();
}
Наслаждайтесь!
Массимо
var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
Взгляните на некоторые безумные примеры синтаксического анализа в 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
int
в большинстве систем с прямым порядком байтов. Поэтому перед преобразованием вы должны перевернуть байты. Смотрите мой ответ для правильных преобразований. Кроме того, даже для IPv4 неint
может содержать адреса больше, чем127.255.255.255
, например, широковещательный адрес, поэтому используйте расширениеuint
.