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.
La solución debe estar en C #.
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.
La solución debe estar en C #.
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:
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;
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));
}
}
Aquí hay un par de métodos para convertir de IPv4 a un número entero correcto y viceversa:
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();
}
Ejemplo
ConvertFromIpAddressToInteger("255.255.255.254"); // 4294967294
ConvertFromIntegerToIpAddress(4294967294); // 255.255.255.254
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
, an int
no 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
.
1.1.1.1
porque su matriz de bytes es palindrómica. Pruebe con los no palindrómicos como 127.0.0.1
o 192.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/aR6fhc
System.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
[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
Entonces, para convertir la dirección IP 64.233.187.99, puede hacer:
(64 = 0x40) << 24 == 0x40000000
(233 = 0xE9) << 16 == 0x00E90000
(187 = 0xBB) << 8 == 0x0000BB00
(99 = 0x63) == 0x00000063
---------- =|
0x40E9BB63
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);
}
LayoutKind.Explicit
y FieldOffset
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:
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()
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 ().
IEnumerable
. El código anterior está perfectamente bien.
Como nadie publicó el código que usa BitConverter
y 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()));
uint
si desea los 32 bits de datos como un número sin firmar, un int
es capaz de contener la misma información. Si desea almacenarlo en una base de datos y int
es más adecuado, necesita bigint
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 un int
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 un uint
.
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.
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;
}
El reverso de la función de Davy Landman
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;
}
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.
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;
}
}
Con UInt32 en el formato little-endian adecuado, aquí hay dos funciones de conversión simples:
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();
}
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.
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");
}
}
Pruebas unitarias:
[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);
}
}
Si estaba interesado en la función, no solo la respuesta, aquí es cómo se hace:
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);
}
con first
través fourth
siendo los segmentos de la dirección 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));
}
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!):
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();
}
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
Creo que esto está mal: "65536" ==> 0.0.255.255 "Debería ser:" 65535 "==> 0.0.255.255" o "65536" ==> 0.1.0.0 "
@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
static uint ToInt(string addr)
{
return BitConverter.ToUInt32(IPAddress.Parse(addr).GetAddressBytes(), 0);
}
static string ToAddr(uint address)
{
return new IPAddress(address).ToString();
}
¡Disfrutar!
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);
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
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
.