¿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.

La solución debe estar en C #.

GateKiller
fuente
15
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;

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));
    }
}
Barry Kelly
fuente
2
Confirmado en Opera 11 en Ubuntu Linux 10.04: convierte el int de nuevo a la forma familiar wxyz, y funciona.
Piskvor salió del edificio el
6
IPAddress.Address está obsoleto a partir de .Net 4.0.
Erik Philips
@ErikPhilips ¿Eso importa si solo usa IPv4?
Ray
Esa es una decisión arquitectónica. Tiene sus pros y sus contras, y los comentarios no son el mejor lugar para discutir ese tema específico.
Erik Philips
1
puede usar BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () para arreglar IPAddress.Address es obsoleto
Mhmd
43

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 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.

Saeb Amini
fuente
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)] 
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);
}
Davy Landman
fuente
1
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.
Mateusz
12

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();
}
Rubens Farias
fuente
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()));
Guffa
fuente
2
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.

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;
}
Qiong Wu
fuente
me parece la mejor solución, sin embargo, cambiaría 1 línea, lo haría byte [] bytes = ip.MapToIPv4 (). GetAddressBytes ();
Walter Vehoeven
4

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;
}
Aris Schizas
fuente
¿podrías explicar más sobre esto?
Developerium
3

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;
    }
}
Coolcoder
fuente
Siempre que pueda hacer la conversión en ambos sentidos, no veo por qué el número de salida tiene que ser correcto siempre que sea coherente.
GateKiller
Depende de para qué quieras usar el número. No puede usar la otra conversión para hacer una consulta> = y <= para encontrar una IP ..
Coolcoder
2

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();
}
DanM7
fuente
2

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);
    }
}
Doug Clutter
fuente
1

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 firsttravés fourthsiendo los segmentos de la dirección IPv4.

Andrew Hare
fuente
1
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
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;
}
xadd
fuente
1
    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.

Gerard ONeill
fuente
0

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


fuente
0

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
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

Massimo
fuente
0

Suponiendo que tiene una dirección IP en formato de cadena (por ejemplo, 254.254.254.254)

string[] vals = inVal.Split('.');
uint output = 0;
for (byte i = 0; i < vals.Length; i++) output += (uint)(byte.Parse(vals[i]) << 8 * (vals.GetUpperBound(0) - i));
Andrés
fuente
0
var ipAddress = "10.101.5.56";

var longAddress = long.Parse(string.Join("", ipAddress.Split('.').Select(x => x.PadLeft(3, '0'))));

Console.WriteLine(longAddress);

Salida: 10101005056

xagyg
fuente
0
var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
shzy2012
fuente
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).

Ondřej
fuente
-1

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

abelenky
fuente
Esta no es realmente una respuesta. ¿Por qué no ponerlo como comentario en una de las principales respuestas?
DanM7