UWAGA : liczby całkowite wynikające z zaakceptowanej odpowiedzi będą błędne , ponieważ adresy IP są w kolejności sieciowej (big-endian), podczas gdy ints 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, intmoże nie posiadać adresy większe niż 127.255.255.255, na przykład adres broadcast, więc użyć uint.
Saeb Amini
Odpowiedzi:
137
32-bitowe liczby całkowite bez znaku to adresy IPv4. Tymczasem IPAddress.Addresswł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;classApp{staticlongToInt(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);}staticstringToAddr(long address){returnIPAddress.Parse(address.ToString()).ToString();// This also works:// return new IPAddress((uint) IPAddress.HostToNetworkOrder(// (int) address)).ToString();}staticvoidMain(){Console.WriteLine(ToInt("64.233.187.99"));Console.WriteLine(ToAddr(1089059683));}}
Adresy IP są w kolejności sieciowej (big-endian), podczas gdy ints 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 intnie może przechowywać adresów większych niż 127.255.255.255np. Adres rozgłoszeniowy (255.255.255.255), więc użyj uint.
Wydaje się, że nie robi to różnicy w systemie Windows używającym .NET - nie mam pewności co do Mono. Zobacz dotnetfiddle.net/cBIOj2
Jesse,
2
@Jesse zdarza się, że nie robi to różnicy dla twojego wejścia, 1.1.1.1ponieważ jego tablica bajtów jest palindromiczna. Spróbuj z nie-palindromicznymi, takimi jak 127.0.0.1lub 192.168.1.1.
Saeb Amini
Widzę problem. Powtarzające liczby takie jak 1.1.1.1, 2.2.2.2, 123.123.123.123zawsze osiąga się ten sam wynik. Dla potomnych zobacz zaktualizowane skrzypce: dotnetfiddle.net/aR6fhc
Jesse
Chciałbym zauważyć, że musiałem użyć, System.Net.IPAddressaby to zadziałało. Działa świetnie!
Shrout 1
1
@Jesse to nie tylko dla powtarzających się liczb, ale dla wszystkich palindromicznych adresów IP. Więc 2.1.1.2byłoby to samo.
Saeb Amini
40
@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)]structIPv4Address{publicByte A;publicByte B;publicByte C;publicByte 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ć:
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);}
Uważam, że adresy IP zostały określone w ten sposób, aby umożliwić takie zachowanie ... przesunięcia bitowe są znacznie bardziej wydajne na większości mikroprocesorów niż muls i dodaje.
Ape-inago
1
@ Mnożenie Ape-inago przez stałe potęgi dwójki jest zwykle optymalizowane do przesunięć bitowych, fwiw.
Barry Kelly
Zamiast przesuwania bitów możesz użyć LayoutKind.Expliciti FieldOffsetdo odwrócenia kolejności, w jakiej przechowywane są bajty. Oczywiście działa to tylko w przypadku architektury little endian. Przykład na github .
RubberDuck
2
Należy zwrócić uwagę, że intjest 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.
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 ().
Erik Philips,
1
Reverse () jest metodą rozszerzającą IEnumerable. Powyższy kod jest w porządku.
Ant,
3
Ta metoda daje liczby ujemne dla niektórych adresów IP (np. 140.117.0.0)
lightxx
7
Ponieważ nikt nie opublikował kodu, który używa BitConverteri 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()));
Musisz użyć uint, ale to jest tutaj najbardziej poprawna odpowiedź.
RubberDuck
1
@RubberDuck: Musisz użyć tylko uintjeśli chcesz, aby 32 bity danych były liczbą bez znaku, intjest w stanie przechowywać te same informacje. Jeśli chcesz przechowywać go w bazie danych, a intjest lepiej dopasowany, musisz bigintmieć możliwość przechowywania go w postaci bez znaku.
Guffa
1
Uint jest lepszą reprezentacją IMO. Podpisany int wymaga trochę na znak, więc tracisz adresy na samej górze zakresu. Tak, może przechowywać te same dane, ale zostaną wyprowadzone jako liczba ujemna, co nie jest prawidłowym adresem IP, jeśli wpiszesz go w pasku adresu.
RubberDuck,
@RubberDuck: W postaci 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 intna tekst nie daje działającego adresu IP, nie jest dobrym argumentem za jego nieużywaniem.
Guffa
@RubberDuck: To nie jest uint, to tekstowa reprezentacja pliku uint.
Guffa
7
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.
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.
publicdoubleIPAddressToNumber(stringIPaddress){int i;string[] arrDec;double num =0;if(IPaddress==""){return0;}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;}}
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.
publicstaticclassIPAddressExtensions{/// <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>publicstaticuintToUint(thisIPAddress address){if(address.AddressFamily==AddressFamily.InterNetwork|| address.IsIPv4MappedToIPv6){var bytes = address.GetAddressBytes();if(BitConverter.IsLittleEndian)Array.Reverse(bytes);returnBitConverter.ToUInt32(bytes,0);}thrownewArgumentOutOfRangeException("address","Address must be IPv4 or IPv4 mapped to IPv6");}}
Testy jednostkowe:
[TestClass]publicclassIPAddressExtensionsTests{[TestMethod]publicvoidSimpleIp1(){var ip =IPAddress.Parse("0.0.0.15");uint expected =GetExpected(0,0,0,15);Assert.AreEqual(expected, ip.ToUint());}[TestMethod]publicvoidSimpleIp2(){var ip =IPAddress.Parse("0.0.1.15");uint expected =GetExpected(0,0,1,15);Assert.AreEqual(expected, ip.ToUint());}[TestMethod]publicvoidSimpleIpSix1(){var ip =IPAddress.Parse("0.0.0.15").MapToIPv6();uint expected =GetExpected(0,0,0,15);Assert.AreEqual(expected, ip.ToUint());}[TestMethod]publicvoidSimpleIpSix2(){var ip =IPAddress.Parse("0.0.1.15").MapToIPv6();uint expected =GetExpected(0,0,1,15);Assert.AreEqual(expected, ip.ToUint());}[TestMethod]publicvoidHighBits(){var ip =IPAddress.Parse("200.12.1.15").MapToIPv6();uint expected =GetExpected(200,12,1,15);Assert.AreEqual(expected, ip.ToUint());}uintGetExpected(uint a,uint b,uint c,uint d){return(a *256u*256u*256u)+(b *256u*256u)+(c *256u)+(d);}}
Myślę, że byłoby bardziej jasne, gdybyś użył shift zamiast Math.Pow.
Mehrdad Afshari
Spowoduje to zgłoszenie wyjątku przepełnienia, jeśli najpierw jest> 127. Odpowiedź Davy'ego Landmana jest najlepszym sposobem na zrobienie tego.
mhenry1384
int32 jest podpisany, więc zwróci wartość ujemną> 127 dla pierwszego oktetu
Mateusz
1
publicboolTryParseIPv4Address(stringvalue,outuint result){IPAddress ipAddress;if(!IPAddress.TryParse(value,out ipAddress)||(ipAddress.AddressFamily!=System.Net.Sockets.AddressFamily.InterNetwork)){
result =0;returnfalse;}
result =BitConverter.ToUInt32(ipAddress.GetAddressBytes().Reverse().ToArray(),0);returntrue;}
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ć!):
privatestaticstringIpToDecimal2(string ipAddress){// need a shift counterint shift =3;// loop through the octets and compute the decimal versionvar 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.
@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
metoda GetAddressBytes może zwracać bajty w odwrotnej kolejności, w zależności od tego, czy maszyna jest endian, czy endian, więc poprawna instrukcja może być dla niektórych maszyn: long m_Address = (address [0] << 24 | address [1] << 16 | adres [2] << 8 | adres [3]) & 0x0FFFFFFFF
MiguelSlv
0
Zauważyłem, że System.Net.IPAddress ma właściwość Address (System.Int64) i konstruktor, które również akceptują typ danych Int64. Możesz więc użyć tego do konwersji adresu IP na / z formatu liczbowego (chociaż nie Int32, ale Int64).
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
.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: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:
źródło
Oto kilka metod konwersji z protokołu IPv4 na poprawną liczbę całkowitą iz powrotem:
Przykład
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
, aint
nie może przechowywać adresów większych niż127.255.255.255
np. Adres rozgłoszeniowy(255.255.255.255)
, więc użyjuint
.źródło
1.1.1.1
ponieważ jego tablica bajtów jest palindromiczna. Spróbuj z nie-palindromicznymi, takimi jak127.0.0.1
lub192.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/aR6fhcSystem.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ę
Aby przekonwertować adres IP 64.233.187.99, możesz zrobić:
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:
źródło
LayoutKind.Explicit
iFieldOffset
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:
źródło
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:i z powrotem:
źródło
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, aint
jest lepiej dopasowany, musiszbigint
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 zamianyint
na tekst nie daje działającego adresu IP, nie jest dobrym argumentem za jego nieużywaniem.uint
, to tekstowa reprezentacja plikuuint
.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.
źródło
Odwrotność funkcji Davy'ego Landmana
źródło
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.
źródło
Mając UInt32 w odpowiednim formacie little-endian, oto dwie proste funkcje konwersji:
źródło
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.
Testy jednostkowe:
źródło
Jeśli interesowała Cię funkcja, a nie tylko odpowiedź, oto jak to się robi:
ze
first
przezfourth
bycie segmenty adresu IPv4.źródło
źródło
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.
źródło
oto rozwiązanie, które wypracowałem dzisiaj (powinienem był najpierw googlować!):
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
źródło
Myślę, że to źle: "65536" ==> 0.0.255.255 "Powinno być:" 65535 "==> 0.0.255.255" lub "65536" ==> 0.1.0.0 "
źródło
@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
Cieszyć się!
Massimo
źródło
Zakładając, że masz adres IP w postaci ciągu (np. 254.254.254.254)
źródło
Wyjście: 10101005056
źródło
źródło
Zauważyłem, że System.Net.IPAddress ma właściwość Address (System.Int64) i konstruktor, które również akceptują typ danych Int64. Możesz więc użyć tego do konwersji adresu IP na / z formatu liczbowego (chociaż nie Int32, ale Int64).
źródło
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
źródło