Come convertire un indirizzo IPv4 in un numero intero in C #?


99

Sto cercando una funzione che converta un indirizzo IPv4 standard in un numero intero. Punti bonus disponibili per una funzione che farà l'opposto.

La soluzione dovrebbe essere in C #.


15
ATTENZIONE : gli interi risultanti dalla risposta accettata saranno sbagliati , perché gli indirizzi IP sono in ordine di rete (big-endian), mentre ints sono little-endian sulla maggior parte dei sistemi. Quindi è necessario invertire i byte prima della conversione. Vedi la mia risposta per le conversioni corrette. Inoltre, anche per IPv4, un intnon può contenere indirizzi più grandi 127.255.255.255, ad esempio , dell'indirizzo di trasmissione, quindi usa un uint.
Saeb Amini

Risposte:


137

Gli interi senza segno a 32 bit sono indirizzi IPv4. Nel frattempo, la IPAddress.Addressproprietà, sebbene deprecata, è un Int64 che restituisce il valore a 32 bit senza segno dell'indirizzo IPv4 (il problema è che è in ordine di byte di rete, quindi è necessario scambiarlo).

Ad esempio, il mio google.com locale è all'indirizzo 64.233.187.99. È equivalente a:

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

E senza dubbio, http: // 1089059683 / funziona come previsto (almeno in Windows, testato con IE, Firefox e Chrome; non funziona su iPhone però).

Ecco un programma di test per mostrare entrambe le conversioni, incluso lo scambio di byte di rete / 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));
    }
}

2
Confermato in Opera 11 su Ubuntu Linux 10.04: converte nuovamente int nel familiare formato wxyz e funziona.
Piskvor ha lasciato l'edificio il

6
IPAddress.Address è obsoleto a partire da .Net 4.0.
Erik Philips

@ErikPhilips È importante se usi solo IPv4?
Ray

Questa è una decisione architettonica. Ha i suoi pro e contro ei commenti non sono il luogo migliore per discutere di quel problema specifico.
Erik Philips

1
puoi usare BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () per correggere IPAddress.Address è obsoleto
Mhmd

43

Ecco un paio di metodi per convertire da IPv4 a un numero intero corretto e 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();
}

Esempio

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

Spiegazione

Gli indirizzi IP sono in ordine di rete (big-endian), mentre gli ints sono little-endian su Windows, quindi per ottenere un valore corretto, è necessario invertire i byte prima della conversione su un sistema little-endian.

Inoltre, anche per IPv4, un intnon può contenere indirizzi più grandi 127.255.255.255, ad esempio (255.255.255.255), dell'indirizzo di trasmissione , quindi usa a uint.


Questo in realtà non sembra fare la differenza su Windows che utilizza .NET - non sono sicuro di Mono. Vedi dotnetfiddle.net/cBIOj2
Jesse

2
@ Jesse sembra che non faccia la differenza per il tuo input 1.1.1.1perché il suo array di byte è palindromico. Prova con quelli non palindromici come 127.0.0.1o 192.168.1.1.
Saeb Amini

Capisco il problema. Numeri ripetuti 1.1.1.1, 2.2.2.2, 123.123.123.123producono sempre lo stesso risultato. Per i posteri, guarda il violino aggiornato: dotnetfiddle.net/aR6fhc
Jesse

Mi piace notare che ho dovuto usare System.Net.IPAddressper farlo funzionare. Funziona alla grande!
Shrout1

1
@ Jesse non sarebbe solo per ripetere i numeri, ma per tutti gli indirizzi IP palindromici . Quindi 2.1.1.2sarebbe lo stesso.
Saeb Amini

40

@Barry Kelly e @Andrew Hare, in realtà, non penso che moltiplicare sia il modo più chiaro per farlo (tutto abbastanza corretto).

Un indirizzo IP "formattato" Int32 può essere visto come la seguente struttura

[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

Quindi per convertire l'indirizzo ip 64.233.187.99 potresti fare:

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

quindi potresti sommarli usando + o potresti binairy o insieme. Il risultato è 0x40E9BB63 che è 1089059683 (secondo me guardando in esadecimale è molto più facile vedere i byte)

Quindi potresti scrivere la funzione come:

int ipToInt(int first, int second, 
    int third, int fourth)
{
    return (first << 24) | (second << 16) | (third << 8) | (fourth);
}

1
Credo che gli ip siano stati specificati in questo modo per consentire specificamente tale comportamento ... i bit shift sono molto più efficienti sulla maggior parte dei microprocessori rispetto ai muls e agli add.
Ape-inago

1
@ Le moltiplicazioni di Ape-inago per potenze costanti di due sono normalmente ottimizzate in spostamenti di bit, fwiw.
Barry Kelly

Invece di spostare i bit, è possibile utilizzare LayoutKind.Explicite FieldOffsetper invertire l'ordine in cui vengono memorizzati i byte. Ovviamente funziona solo per l'architettura Little Endian. Esempio su GitHub .
RubberDuck

2
Devo sottolineare che intè firmato, quindi se sposti 192 di 24 bit otterrai un numero intero negativo, quindi questo codice è rotto per ottetto alto con bit alto al primo posto.
Mateusz

12

Prova questo:

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()restituisce void, quindi non puoi chiamarlo ToArray()(per futuri lettori). Assegna invece un valore ai byte invertiti, quindi puoi chiamare ToArray ().
Erik Philips

1
Reverse () è il metodo di estensione di IEnumerable. Il codice sopra è perfettamente corretto.
Ant

3
Questo metodo produce numeri negativi per alcuni IP (ad esempio 140.117.0.0)
lightxx

7

Poiché nessuno ha pubblicato il codice che utilizza BitConvertere controlla effettivamente l'endianness, ecco qui:

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

e ritorno:

byte[] ip = BitConverter.GetBytes(num);
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
string address = String.Join(".", ip.Select(n => n.ToString()));

2
Devi usare un uint, ma questa è la risposta più corretta qui.
RubberDuck

1
@RubberDuck: è necessario utilizzare solo uintse si desidera che i 32 bit di dati siano un numero senza segno, an intè in grado di contenere le stesse informazioni. Se si desidera archiviarlo in un database intè più adatto, è necessario bigintper poterlo memorizzare nella forma non firmata.
Guffa

1
Un uint è una rappresentazione migliore IMO. Un int firmato ha bisogno di un po 'per il segno, quindi perdi gli indirizzi nella parte superiore dell'intervallo. Sì, può contenere gli stessi dati, ma verrà emesso come un numero negativo, che non è un IP valido se lo digiti nella barra degli indirizzi.
RubberDuck

@RubberDuck: Nella forma di un uintnon è possibile digitarlo nella barra degli indirizzi, devi prima convertirlo in forma di testo per farlo. Solo perché usare la forma più semplice di trasformare un intin testo non produce un indirizzo IP funzionante non è un buon argomento per non usarlo.
Guffa

@RubberDuck: Non è un uint, è la rappresentazione testuale di un file uint.
Guffa

7

Ho riscontrato alcuni problemi con le soluzioni descritte, quando ho affrontato indirizzi IP con un valore molto grande. Il risultato sarebbe che l'oggetto byte [0] * 16777216 andrebbe in overflow e diventerebbe un valore int negativo. ciò che ha risolto il problema per me è stata un'operazione di fusione di tipo semplice.

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

mi sembra la soluzione migliore, tuttavia cambierei 1 riga, farei byte [] bytes = ip.MapToIPv4 (). GetAddressBytes ();
Walter Vehoeven

4

Il contrario della funzione di 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;
}

potresti spiegare di più su questo?
Developerium

3

La mia domanda era chiusa, non ho idea del perché. La risposta accettata qui non è la stessa di cui ho bisogno.

Questo mi dà il valore intero corretto per un 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;
    }
}

Finché puoi eseguire la conversione in entrambi i modi, non vedo perché il numero di output deve essere corretto fintanto che è coerente.
GateKiller

Dipende da cosa vuoi usare il numero. Non puoi utilizzare l'altra conversione per eseguire una query> = e <= per trovare un IP ..
Coolcoder

2

Con l'UInt32 nel formato Little Endian corretto, ecco due semplici funzioni di conversione:

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

2

Assemblato molte delle risposte precedenti in un metodo di estensione che gestisce l'Endianness della macchina e gestisce gli indirizzi IPv4 mappati su 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");
    }
}

Test unitari:

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

1

Se foste interessati alla funzione non solo la risposta ecco come si fa:

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 firstattraverso fourthi segmenti dell'indirizzo IPv4.


1
Penso che sarebbe più chiaro se avessi usato Shift invece di Math.Pow.
Mehrdad Afshari,

Questo genererà un'eccezione di overflow se il primo è> 127. La risposta di Davy Landman è il modo migliore per farlo.
mhenry1384

int32 è firmato quindi restituirà un valore negativo> 127 per il primo ottetto
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;
}

1
    public static Int32 getLongIPAddress(string ipAddress)
    {
        return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
    }

L'esempio sopra sarebbe il modo in cui vado .. L'unica cosa che potresti dover fare è convertire in un UInt32 per scopi di visualizzazione, o per scopi di stringa, incluso usarlo come un indirizzo lungo in forma di stringa.

Che è ciò che è necessario quando si utilizza la funzione IPAddress.Parse (String). Sospiro.


0

ecco una soluzione che ho elaborato oggi (avrei dovuto cercare prima su 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();
    }

sto usando LINQ, lambda e alcune delle estensioni sui generici, quindi mentre produce lo stesso risultato utilizza alcune delle nuove funzionalità del linguaggio e puoi farlo in tre righe di codice.

ho la spiegazione sul mio blog se sei interessato.

applausi, -jc


0

Penso che sia sbagliato: "65536" ==> 0.0.255.255 "Dovrebbe essere:" 65535 "==> 0.0.255.255" o "65536" ==> 0.1.0.0 "


0

@Davy Ladman la tua soluzione con shift è valida ma solo per ip che iniziano con numero minore o uguale a 99, infatti il ​​primo ottetto deve essere lanciato fino a long.

Comunque riconvertire con il tipo lungo è abbastanza difficile perché memorizzare 64 bit (non 32 per Ip) e riempire 4 byte con zeri

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

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

Godere!

Massimo


0

Supponendo che tu abbia un indirizzo IP in formato stringa (es. 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));

0
var ipAddress = "10.101.5.56";

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

Console.WriteLine(longAddress);

Uscita: 10101005056


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

il metodo GetAddressBytes può restituire i byte in ordine inverso, a seconda che la macchina sia endian o endianness quindi l'istruzione corretta potrebbe essere per alcune macchine: long m_Address = (address [0] << 24 | address [1] << 16 | address [2] << 8 | address [3]) & 0x0FFFFFFFF
MiguelSlv

0

Ho notato che System.Net.IPAddress ha la proprietà Address (System.Int64) e il costruttore, che accettano anche il tipo di dati Int64. Quindi puoi usarlo per convertire l'indirizzo IP in / dal formato numerico (sebbene non Int32, ma Int64).


-1

Dai un'occhiata ad alcuni dei folli esempi di analisi in IPAddress.Parse di .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


Questa non è davvero una risposta. Perché non metterlo come commento su una delle risposte migliori?
DanM7
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.