¿Cómo convertir una dirección IPv4 en un número entero en C #?
99
Estoy buscando una función que convierta una dirección IPv4 estándar en un entero. Puntos de bonificación disponibles para una función que hará lo contrario.
ADVERTENCIA : los números enteros resultantes de la respuesta aceptada serán incorrectos , porque las direcciones IP están en orden de red (big-endian), mientras que ints son little-endian en la mayoría de los sistemas. Por lo tanto, debe invertir los bytes antes de convertir. Vea mi respuesta para las conversiones correctas. Además, incluso para IPv4, an intno puede contener direcciones mayores que 127.255.255.255, por ejemplo , la dirección de transmisión, así que use a uint.
Saeb Amini
Respuestas:
137
Los enteros de 32 bits sin signo son direcciones IPv4. Mientras tanto, la IPAddress.Addresspropiedad, aunque obsoleta, es un Int64 que devuelve el valor de 32 bits sin firmar de la dirección IPv4 (el problema es que está en el orden de bytes de la red, por lo que debe intercambiarlo).
Por ejemplo, mi google.com local está en 64.233.187.99. Eso es equivalente a:
64*2^24+233*2^16+187*2^8+99=1089059683
Y de hecho, http: // 1089059683 / funciona como se esperaba (al menos en Windows, probado con IE, Firefox y Chrome; aunque no funciona en iPhone).
Aquí hay un programa de prueba para mostrar ambas conversiones, incluido el intercambio de bytes de red / host:
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));}}
Las direcciones IP están en orden de red (big-endian), mientras que las ints son little-endian en Windows, por lo que para obtener un valor correcto, debe invertir los bytes antes de realizar la conversión en un sistema little-endian.
Además, incluso para IPv4, an intno puede contener direcciones más grandes que 127.255.255.255, por ejemplo, la dirección de transmisión (255.255.255.255), así que use a uint.
Esto en realidad no parece hacer una diferencia en Windows usando .NET, no estoy seguro de Mono. Ver dotnetfiddle.net/cBIOj2
Jesse
2
@Jesse sucede que no hace una diferencia para su entrada 1.1.1.1porque su matriz de bytes es palindrómica. Pruebe con los no palindrómicos como 127.0.0.1o 192.168.1.1.
Saeb Amini
Veo el problema. Números repetidos tales como 1.1.1.1, 2.2.2.2, 123.123.123.123siempre producen el mismo resultado. Para la posteridad, vea el violín actualizado: dotnetfiddle.net/aR6fhc
Jesse
Me gusta señalar que tuve que usar System.Net.IPAddresspara que esto funcione. ¡Funciona genial!
Shrout1
1
@Jesse, eso no sería solo para números repetidos, sino para todas las direcciones IP palindrómicas . Entonces 2.1.1.2sería lo mismo.
Saeb Amini
40
@Barry Kelly y @Andrew Hare, en realidad, no creo que multiplicar sea la forma más clara de hacer esto (todo lo correcto).
Una dirección IP "formateada" Int32 puede verse como la siguiente estructura
[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
Entonces, para convertir la dirección IP 64.233.187.99, puede hacer:
para que pueda sumarlos usando + o binairy o juntos. Dando como resultado 0x40E9BB63 que es 1089059683. (En mi opinión, mirar en hexadecimal es mucho más fácil ver los bytes)
Entonces podrías escribir la función como:
int ipToInt(int first,int second,int third,int fourth){return(first <<24)|(second <<16)|(third <<8)|(fourth);}
Creo que las ip se especificaron de esa manera para permitir específicamente tal comportamiento ... los cambios de bits son mucho más eficientes en la mayoría de los microprocesadores que los muls y agrega.
Ape-inago
1
Las multiplicaciones de @ Ape-inago por potencias constantes de dos normalmente se optimizan en cambios de bits, fwiw.
Barry Kelly
En lugar de desplazamiento de bits, puede utilizar LayoutKind.Explicity FieldOffsetpara invertir el orden en que se almacenan los bytes. Por supuesto, eso solo funciona para la arquitectura little endian. Ejemplo en github .
RubberDuck
2
Debe señalar que intestá firmado, por lo que si cambia 192 por 24 bits, obtendrá un entero negativo, por lo que este código se rompe para el octeto alto que tiene el bit alto en el primer lugar.
Reverse()devuelve void, por lo que no puede invocarlo ToArray()(para futuros lectores). En su lugar, asigne un valor a los bytes invertidos, luego puede llamar a ToArray ().
Erik Philips
1
Reverse () es el método de extensión de IEnumerable. El código anterior está perfectamente bien.
Ant
3
Este método produce números negativos para ciertas IP (por ejemplo, 140.117.0.0)
lightxx
7
Como nadie publicó el código que usa BitConvertery realmente verifica el endianness, aquí va:
byte[] ip = address.Split('.').Select(s =>Byte.Parse(s)).ToArray();if(BitConverter.IsLittleEndian){Array.Reverse(ip);}int num =BitConverter.ToInt32(ip,0);
y regreso:
byte[] ip =BitConverter.GetBytes(num);if(BitConverter.IsLittleEndian){Array.Reverse(ip);}string address =String.Join(".", ip.Select(n => n.ToString()));
Necesita usar un uint, pero esta es la respuesta más correcta aquí.
RubberDuck
1
@RubberDuck: Solo necesita usar un uintsi desea los 32 bits de datos como un número sin firmar, un intes capaz de contener la misma información. Si desea almacenarlo en una base de datos y intes más adecuado, necesita bigintpara poder almacenarlo en el formulario sin firmar.
Guffa
1
Un uint es una mejor representación en mi opinión. Un int firmado necesita un poco para el signo, por lo que pierde direcciones en la parte superior del rango. Sí, puede contener los mismos datos, pero se generará como un número negativo, que no es una IP válida si la escribe en la barra de direcciones.
RubberDuck
@RubberDuck: En forma de uint, tampoco puede escribirlo en la barra de direcciones, primero debe convertirlo en formato de texto para hacerlo. El hecho de que usar la forma más simple de convertir un inten texto no produce una dirección IP que funcione no es un buen argumento para no usarlo.
Guffa
@RubberDuck: Eso no es un uint, esa es la representación de texto de un uint.
Guffa
7
Me he encontrado con algunos problemas con las soluciones descritas, al enfrentar direcciones IP con un valor muy grande. El resultado sería que el byte [0] * 16777216 thingy se desbordaría y se convertiría en un valor int negativo. lo que me lo arregló es la operación de fundición de un tipo simple.
Mi pregunta fue cerrada, no tengo idea de por qué. La respuesta aceptada aquí no es la misma que necesito.
Esto me da el valor entero correcto para una 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;}}
Reunió varias de las respuestas anteriores en un método de extensión que maneja el Endianness de la máquina y maneja las direcciones IPv4 que fueron asignadas a 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");}}
Pruebas unitarias:
[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);}}
Creo que sería más claro si hubiera utilizado shift en lugar de Math.Pow.
Mehrdad Afshari
Esto lanzará una excepción de desbordamiento si primero es> 127. La respuesta de Davy Landman es la mejor manera de hacer esto.
mhenry1384
int32 está firmado, por lo que devolverá un valor negativo> 127 para el primer octeto
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;}
El ejemplo anterior sería el camino a seguir. Lo único que podría tener que hacer es convertir a UInt32 con fines de visualización o con fines de cadena, incluido su uso como una dirección larga en forma de cadena.
Que es lo que se necesita cuando se usa la función IPAddress.Parse (String). Suspiro.
aquí hay una solución que resolví hoy (¡debería haber buscado en Google primero!):
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();}
Estoy usando LINQ, lambda y algunas de las extensiones en genéricos, así que aunque produce el mismo resultado, usa algunas de las nuevas características del lenguaje y puede hacerlo en tres líneas de código.
tengo la explicación en mi blog si estás interesado.
@Davy Ladman, su solución con shift es actual, pero solo para ip que comience con un número menor o igual a 99, de hecho, el primer octecto debe ser largo.
De todos modos, volver a convertir con tipo largo es bastante difícil porque almacena 64 bits (no 32 para Ip) y llena 4 bytes con ceros
el método GetAddressBytes puede devolver los bytes en el orden inverso, dependiendo de si la máquina es endian o endian-ness, por lo que la declaración correcta puede ser para algunas máquinas: long m_Address = (dirección [0] << 24 | dirección [1] << 16 | dirección [2] << 8 | dirección [3]) & 0x0FFFFFFFF
MiguelSlv
0
Noté que System.Net.IPAddress tiene la propiedad Address (System.Int64) y el constructor, que también aceptan el tipo de datos Int64. Por lo tanto, puede usar esto para convertir la dirección IP a / desde formato numérico (aunque no Int32, sino Int64).
int
s son little-endian en la mayoría de los sistemas. Por lo tanto, debe invertir los bytes antes de convertir. Vea mi respuesta para las conversiones correctas. Además, incluso para IPv4, anint
no puede contener direcciones mayores que127.255.255.255
, por ejemplo , la dirección de transmisión, así que use auint
.Respuestas:
Los enteros de 32 bits sin signo son direcciones IPv4. Mientras tanto, la
IPAddress.Address
propiedad, aunque obsoleta, es un Int64 que devuelve el valor de 32 bits sin firmar de la dirección IPv4 (el problema es que está en el orden de bytes de la red, por lo que debe intercambiarlo).Por ejemplo, mi google.com local está en
64.233.187.99
. Eso es equivalente a:Y de hecho, http: // 1089059683 / funciona como se esperaba (al menos en Windows, probado con IE, Firefox y Chrome; aunque no funciona en iPhone).
Aquí hay un programa de prueba para mostrar ambas conversiones, incluido el intercambio de bytes de red / host:
fuente
Aquí hay un par de métodos para convertir de IPv4 a un número entero correcto y viceversa:
Ejemplo
Explicación
Las direcciones IP están en orden de red (big-endian), mientras que las
int
s son little-endian en Windows, por lo que para obtener un valor correcto, debe invertir los bytes antes de realizar la conversión en un sistema little-endian.Además, incluso para
IPv4
, anint
no puede contener direcciones más grandes que127.255.255.255
, por ejemplo, la dirección de transmisión(255.255.255.255)
, así que use auint
.fuente
1.1.1.1
porque su matriz de bytes es palindrómica. Pruebe con los no palindrómicos como127.0.0.1
o192.168.1.1
.1.1.1.1
,2.2.2.2
,123.123.123.123
siempre producen el mismo resultado. Para la posteridad, vea el violín actualizado: dotnetfiddle.net/aR6fhcSystem.Net.IPAddress
para que esto funcione. ¡Funciona genial!2.1.1.2
sería lo mismo.@Barry Kelly y @Andrew Hare, en realidad, no creo que multiplicar sea la forma más clara de hacer esto (todo lo correcto).
Una dirección IP "formateada" Int32 puede verse como la siguiente estructura
Entonces, para convertir la dirección IP 64.233.187.99, puede hacer:
para que pueda sumarlos usando + o binairy o juntos. Dando como resultado 0x40E9BB63 que es 1089059683. (En mi opinión, mirar en hexadecimal es mucho más fácil ver los bytes)
Entonces podrías escribir la función como:
fuente
LayoutKind.Explicit
yFieldOffset
para invertir el orden en que se almacenan los bytes. Por supuesto, eso solo funciona para la arquitectura little endian. Ejemplo en github .int
está firmado, por lo que si cambia 192 por 24 bits, obtendrá un entero negativo, por lo que este código se rompe para el octeto alto que tiene el bit alto en el primer lugar.Prueba estos:
fuente
Reverse()
devuelve void, por lo que no puede invocarloToArray()
(para futuros lectores). En su lugar, asigne un valor a los bytes invertidos, luego puede llamar a ToArray ().IEnumerable
. El código anterior está perfectamente bien.Como nadie publicó el código que usa
BitConverter
y realmente verifica el endianness, aquí va:y regreso:
fuente
uint
si desea los 32 bits de datos como un número sin firmar, unint
es capaz de contener la misma información. Si desea almacenarlo en una base de datos yint
es más adecuado, necesitabigint
para poder almacenarlo en el formulario sin firmar.uint
, tampoco puede escribirlo en la barra de direcciones, primero debe convertirlo en formato de texto para hacerlo. El hecho de que usar la forma más simple de convertir unint
en texto no produce una dirección IP que funcione no es un buen argumento para no usarlo.uint
, esa es la representación de texto de unuint
.Me he encontrado con algunos problemas con las soluciones descritas, al enfrentar direcciones IP con un valor muy grande. El resultado sería que el byte [0] * 16777216 thingy se desbordaría y se convertiría en un valor int negativo. lo que me lo arregló es la operación de fundición de un tipo simple.
fuente
El reverso de la función de Davy Landman
fuente
Mi pregunta fue cerrada, no tengo idea de por qué. La respuesta aceptada aquí no es la misma que necesito.
Esto me da el valor entero correcto para una IP.
fuente
Con UInt32 en el formato little-endian adecuado, aquí hay dos funciones de conversión simples:
fuente
Reunió varias de las respuestas anteriores en un método de extensión que maneja el Endianness de la máquina y maneja las direcciones IPv4 que fueron asignadas a IPv6.
Pruebas unitarias:
fuente
Si estaba interesado en la función, no solo la respuesta, aquí es cómo se hace:
con
first
travésfourth
siendo los segmentos de la dirección IPv4.fuente
fuente
El ejemplo anterior sería el camino a seguir. Lo único que podría tener que hacer es convertir a UInt32 con fines de visualización o con fines de cadena, incluido su uso como una dirección larga en forma de cadena.
Que es lo que se necesita cuando se usa la función IPAddress.Parse (String). Suspiro.
fuente
aquí hay una solución que resolví hoy (¡debería haber buscado en Google primero!):
Estoy usando LINQ, lambda y algunas de las extensiones en genéricos, así que aunque produce el mismo resultado, usa algunas de las nuevas características del lenguaje y puede hacerlo en tres líneas de código.
tengo la explicación en mi blog si estás interesado.
salud, -jc
fuente
Creo que esto está mal: "65536" ==> 0.0.255.255 "Debería ser:" 65535 "==> 0.0.255.255" o "65536" ==> 0.1.0.0 "
fuente
@Davy Ladman, su solución con shift es actual, pero solo para ip que comience con un número menor o igual a 99, de hecho, el primer octecto debe ser largo.
De todos modos, volver a convertir con tipo largo es bastante difícil porque almacena 64 bits (no 32 para Ip) y llena 4 bytes con ceros
¡Disfrutar!
Massimo
fuente
Suponiendo que tiene una dirección IP en formato de cadena (por ejemplo, 254.254.254.254)
fuente
Salida: 10101005056
fuente
fuente
Noté que System.Net.IPAddress tiene la propiedad Address (System.Int64) y el constructor, que también aceptan el tipo de datos Int64. Por lo tanto, puede usar esto para convertir la dirección IP a / desde formato numérico (aunque no Int32, sino Int64).
fuente
Eche un vistazo a algunos de los locos ejemplos de análisis en IPAddress.Parse de .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
fuente