Como converter um endereço IPv4 em um número inteiro em C #?

99

Estou procurando uma função que irá converter um endereço IPv4 padrão em um inteiro. Pontos de bônus disponíveis para uma função que fará o oposto.

A solução deve estar em C #.

GateKiller
fonte
15
AVISO : os inteiros resultantes da resposta aceita estarão errados , porque os endereços IP estão na ordem da rede (big-endian), enquanto ints são little-endian na maioria dos sistemas. Portanto, você deve inverter os bytes antes de converter. Veja minha resposta para conversões corretas. Além disso, mesmo para IPv4, um intnão pode conter endereços maiores que 127.255.255.255, por exemplo , o endereço de broadcast, então use a uint.
Saeb Amini

Respostas:

137

Inteiros não assinados de 32 bits são endereços IPv4. Enquanto isso, oIPAddress.Address propriedade, embora obsoleta, é um Int64 que retorna o valor de 32 bits não assinado do endereço IPv4 (o problema é que está na ordem de bytes da rede, então você precisa trocá-lo).

Por exemplo, meu google.com local é em 64.233.187.99. Isso é equivalente a:

64*2^24 + 233*2^16 + 187*2^8 + 99
= 1089059683

E realmente, http: // 1089059683 / funciona como esperado (pelo menos no Windows, testado com IE, Firefox e Chrome; embora não funcione no iPhone).

Este é um programa de teste para mostrar ambas as conversões, incluindo a troca de bytes de rede / 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
fonte
2
Confirmado no Opera 11 no Ubuntu Linux 10.04: ele converte o int de volta para a forma wxyz familiar e funciona.
Piskvor saiu do prédio em
6
IPAddress.Address é obsoleto a partir do .Net 4.0.
Erik Philips
@ErikPhilips Isso importa se você usa apenas IPv4?
Ray
Essa é uma decisão arquitetônica. Tem seus prós e contras, e os comentários não são o melhor local para discutir esse problema específico.
Erik Philips
1
você pode usar BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () para corrigir IPAddress.Address é obsoleto
Mhmd
43

Aqui está um par de métodos para converter IPv4 em um número inteiro correto e vice-versa:

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

Exemplo

ConvertFromIpAddressToInteger("255.255.255.254"); // 4294967294
ConvertFromIntegerToIpAddress(4294967294); // 255.255.255.254

Explicação

Os endereços IP estão na ordem da rede (big-endian), enquanto ints são little-endian no Windows, portanto, para obter um valor correto, você deve reverter os bytes antes de converter em um sistema little-endian.

Além disso, mesmo para IPv4an intnão pode conter endereços maiores do que 127.255.255.255, por exemplo, o endereço de broadcast (255.255.255.255), então use a uint.

Saeb Amini
fonte
Na verdade, isso não parece fazer diferença no Windows usando .NET - não tenho certeza sobre o Mono. Consulte dotnetfiddle.net/cBIOj2
Jesse
2
@Jesse acontece de não fazer diferença para sua entrada de 1.1.1.1porque seu array de bytes é palíndromo. Experimente com os não palíndrômicos como 127.0.0.1ou 192.168.1.1.
Saeb Amini
Eu vejo o problema. Números repetidos, tais como 1.1.1.1, 2.2.2.2, 123.123.123.123sempre produzir o mesmo resultado. Para a posteridade, veja o violino atualizado: dotnetfiddle.net/aR6fhc
Jesse
Gosto de observar que eu tive que usar System.Net.IPAddresspara fazer isso funcionar. Funciona bem!
Shrout1
1
@Jesse isso não seria apenas para repetir números, mas para todos os endereços IP palíndromos . Então 2.1.1.2seria o mesmo.
Saeb Amini
40

@Barry Kelly e @Andrew Hare, na verdade, não acho que a multiplicação seja a maneira mais clara de fazer isso (totalmente correto).

Um endereço IP "formatado" Int32 pode ser visto como a seguinte estrutura

[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

Portanto, para converter o endereço IP 64.233.187.99, você pode fazer:

(64  = 0x40) << 24 == 0x40000000
(233 = 0xE9) << 16 == 0x00E90000
(187 = 0xBB) << 8  == 0x0000BB00
(99  = 0x63)       == 0x00000063
                      ---------- =|
                      0x40E9BB63

então você pode somar usando + ou você pode binairy ou eles juntos. Resultando em 0x40E9BB63 que é 1089059683. (Na minha opinião, olhando em hexadecimal é muito mais fácil ver os bytes)

Então você pode escrever a função como:

int ipToInt(int first, int second, 
    int third, int fourth)
{
    return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
Davy Landman
fonte
1
Eu acredito que os ip's foram especificados dessa forma para permitir especificamente tal comportamento ... bit shifts são muito mais eficientes na maioria dos microprocessadores do que muls e add.
Ape-inago
1
@ As multiplicações ape-inago por potências constantes de dois são normalmente otimizadas em trocas de bits, fwiw.
Barry Kelly
Em vez de deslocamento de bits, você pode usar LayoutKind.Explicite FieldOffsetpara inverter a ordem em que os bytes são armazenados. Claro, isso só funciona para a arquitetura little endian. Exemplo no github .
RubberDuck
2
Tem que apontar que inté assinado então se você deslocar 192 por 24 bits você obterá um inteiro negativo então este código é quebrado para octeto alto tendo bit alto em primeiro lugar.
Mateusz
12

Experimente estes:

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
fonte
Reverse()retorna void, então você não pode chamá ToArray()-lo (para leitores futuros). Em vez disso, atribua um valor aos bytes invertidos, então você pode chamar ToArray ().
Erik Philips
1
Reverse () é o método de extensão de IEnumerable. O código acima está perfeitamente ok.
Formiga
3
Este método produz números negativos para determinados IPs (por exemplo, 140.117.0.0)
lightxx
7

Como ninguém postou o código que usa BitConvertere realmente verifica o endianness, aqui vai:

byte[] ip = address.Split('.').Select(s => Byte.Parse(s)).ToArray();
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
int num = BitConverter.ToInt32(ip, 0);

e volta:

byte[] ip = BitConverter.GetBytes(num);
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
string address = String.Join(".", ip.Select(n => n.ToString()));
Guffa
fonte
2
Você precisa usar um uint, mas esta é a resposta mais correta aqui.
RubberDuck
1
@RubberDuck: Você só precisa usar um uintse quiser os 32 bits de dados como um número sem sinal e intfor capaz de conter as mesmas informações. Se você quiser armazená-lo em um banco de dados e intfor mais adequado, você precisa bigintser capaz de armazená-lo na forma não assinada.
Guffa
1
A uint é uma representação melhor IMO. Um int assinado precisa de um pouco para o sinal, então você perde endereços no topo da faixa. Sim, ele pode conter os mesmos dados, mas será gerado como um número negativo, que não é um IP válido se você digitá-lo na barra de endereço.
RubberDuck
@RubberDuck: Na forma de um, uintvocê também não pode digitá-lo na barra de endereço, primeiro você precisa convertê-lo em um texto para fazer isso. Só porque usar a forma mais simples de transformar um intem texto não produz um endereço IP funcional, não é um bom argumento para não usá-lo.
Guffa
@RubberDuck: Não é um uint, é a representação de texto de um uint.
Guffa
7

Eu encontrei alguns problemas com as soluções descritas, ao enfrentar endereços IP com um valor muito grande. O resultado seria que o byte [0] * 16777216 thingy estouraria e se tornaria um valor int negativo. o que consertou para mim foi a operação de fundição de um tipo simples.

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
fonte
me parece a melhor solução, porém eu mudaria 1 linha, faria byte [] bytes = ip.MapToIPv4 (). GetAddressBytes ();
Walter Vehoeven
4

O reverso da função 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
fonte
você poderia explicar mais sobre isso?
Developerium
3

Minha pergunta foi encerrada, não tenho ideia do porquê. A resposta aceita aqui não é a mesma que eu preciso.

Isso me dá o valor inteiro correto para um 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
fonte
Contanto que você possa fazer a conversão dos dois modos, não vejo por que o número de saída tem que estar correto, contanto que seja consistente.
GateKiller de
Depende de para que você deseja usar o número. Você não pode usar a outra conversão para fazer uma consulta> = e <= para encontrar um IP ..
Coolcoder
2

Com o UInt32 no formato little-endian adequado, aqui estão duas funções de conversão 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
fonte
2

Reunimos várias das respostas acima em um método de extensão que lida com o Endianness da máquina e endereços IPv4 que foram mapeados para 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");
    }
}

Testes de unidade:

[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
fonte
1

Se você estava interessado na função, não apenas a resposta aqui é como ela é feita:

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);
}

com firstatravés fourthsendo os segmentos do endereço IPv4.

Andrew Hare
fonte
1
Acho que ficaria mais claro se você usasse shift em vez de Math.Pow.
Mehrdad Afshari
Isso lançará uma exceção de estouro se o primeiro for> 127. A resposta de Davy Landman é a melhor maneira de fazer isso.
mhenry1384
int32 é assinado, portanto, retornará um valor negativo> 127 para o primeiro 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
fonte
1
    public static Int32 getLongIPAddress(string ipAddress)
    {
        return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
    }

O exemplo acima seria o meu caminho. A única coisa que você pode ter que fazer é converter para um UInt32 para fins de exibição ou para fins de string, incluindo usá-lo como um endereço longo na forma de string.

Que é necessário ao usar a função IPAddress.Parse (String). Suspiro.

Gerard ONeill
fonte
0

aqui está uma solução que descobri hoje (deveria ter pesquisado primeiro no Google!):

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

Estou usando LINQ, lambda e algumas das extensões dos genéricos, portanto, embora produza o mesmo resultado, usa alguns dos novos recursos de linguagem e você pode fazer isso em três linhas de código.

eu tenho a explicação no meu blog se você estiver interessado.

saúde, -jc


fonte
0

Acho que isso está errado: "65536" ==> 0.0.255.255 "Deveria ser:" 65535 "==> 0.0.255.255" ou "65536" ==> 0.1.0.0 "


fonte
0

@Davy Ladman, sua solução com shift está correta, mas apenas para ip começando com número menor ou igual a 99, de fato, o primeiro octeto deve ser convertido em longo.

De qualquer forma, converter de volta com o tipo longo é bastante difícil porque armazena 64 bits (não 32 para Ip) e preenche 4 bytes com zeros

static uint ToInt(string addr)
{
   return BitConverter.ToUInt32(IPAddress.Parse(addr).GetAddressBytes(), 0);
}

static string ToAddr(uint address)
{
    return new IPAddress(address).ToString();
}

Aproveitar!

Massimo

Massimo
fonte
0

Supondo que você tenha um endereço IP em formato de string (por exemplo, 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));
Andrew
fonte
0
var ipAddress = "10.101.5.56";

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

Console.WriteLine(longAddress);

Saída: 10101005056

xagyg
fonte
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
fonte
o método GetAddressBytes pode retornar os bytes na ordem inversa, dependendo se a máquina é endian ou endian-ness, então a instrução correta pode ser para algumas máquinas: long m_Address = (endereço [0] << 24 | endereço [1] << 16 | endereço [2] << 8 | endereço [3]) & 0x0FFFFFFFF
MiguelSlv
0

Percebi que System.Net.IPAddress tem a propriedade Address (System.Int64) e o construtor, que também aceita o tipo de dados Int64. Portanto, você pode usar isso para converter o endereço IP de / para o formato numérico (embora não seja Int32, mas Int64).

Ondřej
fonte
-1

Dê uma olhada em alguns dos exemplos malucos de análise em IPAddress.Parse do .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
fonte
Isso não é realmente uma resposta. Por que não comentar sobre uma das principais respostas?
DanM7