Я шукаю функцію, яка перетворить стандартний 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-адреси впорядковані в мережевому порядку (big-endian), в той час як int
s - малоінтенсивні в Windows, тож, щоб отримати правильне значення, необхідно перетворити байти перед перетворенням у систему з малою ендіанією.
Крім того, навіть для IPv4
, не int
можна містити адреси, більші, ніж 127.255.255.255
, наприклад, широкомовна адреса (255.255.255.255)
, тому використовуйте a 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
тож ви можете додати їх за допомогою + або ви можете створити файли або їх разом. У результаті виходить 0x40E9BB63, що становить 1089059683. (На мою думку, дивлячись у шістнадцятковій формі набагато простіше бачити байти)
Таким чином, ви можете записати функцію як:
int ipToInt(int first, int second,
int third, int fourth)
{
return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
LayoutKind.Explicit
та FieldOffset
змінювати порядок, в якому зберігаються байти. Звичайно, це працює лише для маленької архітектури ендіан. Приклад на github .
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()
повертається недійсно, тому ви не можете його закликати 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 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;
}
Зворотна функція Деві Лендмена
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, інфактний перший октект повинен бути доданий до довгого.
У будь-якому випадку перетворити назад з довгим типом досить складно, оскільки зберігати 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);
int
s в малосистемних системах у більшості систем. Тому перед перетворенням потрібно перевернути байти. Дивіться мою відповідь щодо правильних перетворень. Крім того, навіть для IPv4,int
адреса не може містити більше, ніж127.255.255.255
, наприклад, широкомовна адреса, тому використовуйте auint
.