Szukam funkcji, która zamieni standardowy adres IPv4 na liczbę całkowitą. Dodatkowe punkty dostępne za funkcję, która zrobi odwrotnie.
Rozwiązanie powinno być w C #.
Szukam funkcji, która zamieni standardowy adres IPv4 na liczbę całkowitą. Dodatkowe punkty dostępne za funkcję, która zrobi odwrotnie.
Rozwiązanie powinno być w C #.
Odpowiedzi:
32-bitowe liczby całkowite bez znaku to adresy IPv4. Tymczasem IPAddress.Address
właściwość, mimo że jest przestarzała, jest wartością Int64, która zwraca 32-bitową wartość adresu IPv4 bez znaku (haczyk jest w kolejności bajtów sieci, więc musisz ją zamienić).
Na przykład moja lokalna witryna google.com to 64.233.187.99
. To jest równoważne z:
64*2^24 + 233*2^16 + 187*2^8 + 99
= 1089059683
I rzeczywiście, http: // 1089059683 / działa zgodnie z oczekiwaniami (przynajmniej w systemie Windows, testowany z IE, Firefox i Chrome; nie działa jednak na iPhonie).
Oto program testowy pokazujący obie konwersje, w tym zamianę bajtów sieci / hosta:
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));
}
}
Oto kilka metod konwersji z protokołu IPv4 na poprawną liczbę całkowitą iz powrotem:
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();
}
Przykład
ConvertFromIpAddressToInteger("255.255.255.254"); // 4294967294
ConvertFromIntegerToIpAddress(4294967294); // 255.255.255.254
Wyjaśnienie
Adresy IP są w kolejności sieciowej (big-endian), podczas gdy int
s to little-endian w systemie Windows, więc aby uzyskać poprawną wartość, należy odwrócić bajty przed konwersją w systemie little-endian.
Ponadto, nawet dla IPv4
, a int
nie może przechowywać adresów większych niż 127.255.255.255
np. Adres rozgłoszeniowy (255.255.255.255)
, więc użyj uint
.
1.1.1.1
ponieważ jego tablica bajtów jest palindromiczna. Spróbuj z nie-palindromicznymi, takimi jak 127.0.0.1
lub 192.168.1.1
.
1.1.1.1
, 2.2.2.2
, 123.123.123.123
zawsze osiąga się ten sam wynik. Dla potomnych zobacz zaktualizowane skrzypce: dotnetfiddle.net/aR6fhc
System.Net.IPAddress
aby to zadziałało. Działa świetnie!
2.1.1.2
byłoby to samo.
@Barry Kelly i @Andrew Hare, tak naprawdę, nie sądzę, aby mnożenie było najbardziej przejrzystym sposobem na zrobienie tego (wszystko w porządku).
„Sformatowany” adres IP Int32 można zobaczyć jako następującą strukturę
[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
Aby przekonwertować adres IP 64.233.187.99, możesz zrobić:
(64 = 0x40) << 24 == 0x40000000
(233 = 0xE9) << 16 == 0x00E90000
(187 = 0xBB) << 8 == 0x0000BB00
(99 = 0x63) == 0x00000063
---------- =|
0x40E9BB63
więc możesz je dodać za pomocą + lub możesz je połączyć lub obustronnie. Wynik 0x40E9BB63, czyli 1089059683. (Moim zdaniem patrząc w szesnastce dużo łatwiej jest zobaczyć bajty)
Możesz więc zapisać funkcję jako:
int ipToInt(int first, int second,
int third, int fourth)
{
return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
LayoutKind.Explicit
i FieldOffset
do odwrócenia kolejności, w jakiej przechowywane są bajty. Oczywiście działa to tylko w przypadku architektury little endian. Przykład na github .
int
jest on podpisany, więc jeśli przesuniesz 192 na 24 bity, otrzymasz ujemną liczbę całkowitą, więc ten kod jest uszkodzony dla wysokiego oktetu, który ma wysoki bit na pierwszym miejscu.
Wypróbuj te:
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()
zwraca void, więc nie możesz go wywołać ToArray()
(dla przyszłych czytelników). Zamiast tego przypisz wartość do odwróconych bajtów, a następnie możesz wywołać ToArray ().
IEnumerable
. Powyższy kod jest w porządku.
Ponieważ nikt nie opublikował kodu, który używa BitConverter
i faktycznie sprawdza endianness, oto:
byte[] ip = address.Split('.').Select(s => Byte.Parse(s)).ToArray();
if (BitConverter.IsLittleEndian) {
Array.Reverse(ip);
}
int num = BitConverter.ToInt32(ip, 0);
i z powrotem:
byte[] ip = BitConverter.GetBytes(num);
if (BitConverter.IsLittleEndian) {
Array.Reverse(ip);
}
string address = String.Join(".", ip.Select(n => n.ToString()));
uint
jeśli chcesz, aby 32 bity danych były liczbą bez znaku, int
jest w stanie przechowywać te same informacje. Jeśli chcesz przechowywać go w bazie danych, a int
jest lepiej dopasowany, musisz bigint
mieć możliwość przechowywania go w postaci bez znaku.
uint
, której nie możesz również wpisać w pasku adresu, musisz najpierw przekonwertować go na formę tekstową, aby to zrobić. Tylko dlatego, że użycie najprostszej formy zamiany int
na tekst nie daje działającego adresu IP, nie jest dobrym argumentem za jego nieużywaniem.
uint
, to tekstowa reprezentacja pliku uint
.
Napotkałem pewne problemy z opisanymi rozwiązaniami, mając adresy IP o bardzo dużej wartości. W rezultacie bajt [0] * 16777216 thingy przepełniłby się i stałby się ujemną wartością int. to, co mnie naprawiło, to prosta operacja odlewania.
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;
}
Odwrotność funkcji Davy'ego Landmana
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;
}
Moje pytanie zostało zamknięte, nie mam pojęcia dlaczego. Przyjęta tutaj odpowiedź nie jest tym samym, czego potrzebuję.
To daje mi poprawną wartość całkowitą dla adresu 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;
}
}
Mając UInt32 w odpowiednim formacie little-endian, oto dwie proste funkcje konwersji:
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();
}
Połączono kilka z powyższych odpowiedzi w metodę rozszerzenia, która obsługuje Endianness maszyny i obsługuje adresy IPv4, które zostały zmapowane na 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");
}
}
Testy jednostkowe:
[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);
}
}
Jeśli interesowała Cię funkcja, a nie tylko odpowiedź, oto jak to się robi:
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);
}
ze first
przez fourth
bycie segmenty adresu 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));
}
Powyższy przykład byłby właściwą drogą. Jedyne, co możesz zrobić, to przekonwertować na UInt32 do celów wyświetlania lub do celów łańcuchowych, w tym użyć go jako długiego adresu w postaci ciągu.
Co jest potrzebne przy korzystaniu z funkcji IPAddress.Parse (String). Westchnienie.
oto rozwiązanie, które wypracowałem dzisiaj (powinienem był najpierw googlować!):
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();
}
Używam LINQ, lambda i niektórych rozszerzeń w generycznych, więc chociaż daje ten sam wynik, używa niektórych nowych funkcji języka i możesz to zrobić w trzech wierszach kodu.
Mam wyjaśnienie na moim blogu, jeśli jesteś zainteresowany.
okrzyki, -jc
Myślę, że to źle: "65536" ==> 0.0.255.255 "Powinno być:" 65535 "==> 0.0.255.255" lub "65536" ==> 0.1.0.0 "
@Davy Ladman Twoje rozwiązanie z przesunięciem jest aktualne, ale tylko dla IP zaczynającego się od numeru mniejszego lub równego 99, w rzeczywistości pierwszy oktekt musi być rzucony zbyt długo.
W każdym razie konwersja z powrotem z typem długim jest dość trudna, ponieważ przechowuj 64 bity (nie 32 dla Ip) i wypełnij 4 bajty zerami
static uint ToInt(string addr)
{
return BitConverter.ToUInt32(IPAddress.Parse(addr).GetAddressBytes(), 0);
}
static string ToAddr(uint address)
{
return new IPAddress(address).ToString();
}
Cieszyć się!
Massimo
var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
Spójrz na niektóre zwariowane przykłady analizowania w .Net's 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
int
s to little-endian w większości systemów. Musisz więc odwrócić bajty przed konwersją. Zobacz moją odpowiedź dotyczącą prawidłowych konwersji. Ponadto, nawet dla IPv4,int
może nie posiadać adresy większe niż127.255.255.255
, na przykład adres broadcast, więc użyćuint
.