Comment convertir une adresse IPv4 en un entier en C #?


99

Je recherche une fonction qui convertira une adresse IPv4 standard en un entier. Points bonus disponibles pour une fonction qui fera le contraire.

La solution doit être en C #.


15
ATTENTION : les entiers résultant de la réponse acceptée seront faux , car les adresses IP sont dans l'ordre du réseau (big-endian), tandis que ints sont little-endian sur la plupart des systèmes. Vous devez donc inverser les octets avant de convertir. Voir ma réponse pour les conversions correctes. De plus, même pour IPv4, un intne peut pas contenir d'adresses plus grandes que 127.255.255.255, par exemple, l'adresse de diffusion, alors utilisez un uint.
Saeb Amini

Réponses:


137

Les entiers non signés 32 bits sont des adresses IPv4. Pendant ce temps, la IPAddress.Addresspropriété, bien que déconseillée, est un Int64 qui renvoie la valeur 32 bits non signée de l'adresse IPv4 (le hic, c'est qu'elle est dans l'ordre des octets du réseau, vous devez donc l'échanger).

Par exemple, mon google.com local est à 64.233.187.99. C'est équivalent à:

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

Et en effet, http: // 1089059683 / fonctionne comme prévu (au moins sous Windows, testé avec IE, Firefox et Chrome; ne fonctionne pas sur iPhone cependant).

Voici un programme de test pour afficher les deux conversions, y compris l'échange d'octets réseau / hôte:

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
Confirmé dans Opera 11 sur Ubuntu Linux 10.04: il reconvertit l'int en une forme familière de wxyz, et ça marche.
Piskvor a quitté le bâtiment

6
IPAddress.Address est obsolète depuis .Net 4.0.
Erik Philips

@ErikPhilips Est-ce important si vous utilisez uniquement IPv4?
Ray

C'est une décision architecturale. Il a ses avantages et ses inconvénients et les commentaires ne sont pas le meilleur endroit pour discuter de cette question spécifique.
Erik Philips

1
vous pouvez utiliser BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () pour corriger IPAddress.Address est obsolète
Mhmd

43

Voici une paire de méthodes pour convertir IPv4 en un entier correct et inversement:

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

Exemple

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

Explication

Les adresses IP sont dans l'ordre du réseau (big-endian), tandis que ints sont little-endian sous Windows, donc pour obtenir une valeur correcte, vous devez inverser les octets avant de convertir sur un système little-endian.

De plus, même pour IPv4, an intne peut pas contenir d'adresses plus grandes que 127.255.255.255, par exemple, l'adresse de diffusion (255.255.255.255), alors utilisez un uint.


Cela ne semble pas réellement faire de différence sur Windows utilisant .NET - je ne suis pas sûr de Mono. Voir dotnetfiddle.net/cBIOj2
Jesse

2
@Jesse cela ne fait pas de différence pour votre entrée 1.1.1.1car son tableau d'octets est palindromique. Essayez avec des non-palindromiques comme 127.0.0.1ou 192.168.1.1.
Saeb Amini

Je vois le problème. Numéros répétés tels que 1.1.1.1, 2.2.2.2, 123.123.123.123donnent toujours le même résultat. Pour la postérité, voir le violon mis à jour: dotnetfiddle.net/aR6fhc
Jesse

Je tiens à noter que je devais utiliser System.Net.IPAddresspour que cela fonctionne. Fonctionne très bien!
Shrout1

1
@Jesse ce ne serait pas seulement pour répéter des nombres, mais pour toutes les adresses IP palindromiques . Ce 2.1.1.2serait pareil.
Saeb Amini

40

@Barry Kelly et @Andrew Hare, en fait, je ne pense pas que multiplier soit le moyen le plus clair de le faire (tout est correct).

Une adresse IP "formatée" Int32 peut être considérée comme la structure suivante

[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

Donc, pour convertir l'adresse IP 64.233.187.99, vous pouvez faire:

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

vous pouvez donc les additionner en utilisant + ou vous pouvez binairy ou les ensemble. Résultat 0x40E9BB63 qui est 1089059683. (À mon avis, en regardant en hexadécimal, il est beaucoup plus facile de voir les octets)

Vous pouvez donc écrire la fonction comme suit:

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

1
Je crois que les ip ont été spécifiés de cette façon pour permettre spécifiquement un tel comportement ... les décalages de bits sont beaucoup plus efficaces sur la plupart des microprocesseurs que les muls et les ajouts.
Ape-inago le

1
@ Les multiplications Ape-inago par des puissances constantes de deux sont normalement optimisées en décalages de bits, fwiw.
Barry Kelly

Au lieu du décalage de bits, vous pouvez utiliser LayoutKind.Explicitet FieldOffsetpour inverser l'ordre dans lequel les octets sont stockés. Bien sûr, cela ne fonctionne que pour l'architecture little endian. Exemple sur github .
RubberDuck

2
Je dois souligner que intc'est signé, donc si vous décalez 192 de 24 bits, vous obtiendrez un entier négatif, donc ce code est cassé pour l'octet haut ayant le bit haut à la première place.
Mateusz le

12

Essayez ceux-ci:

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()renvoie void, vous ne pouvez donc pas y faire appel ToArray()(pour les futurs lecteurs). Au lieu de cela, attribuez une valeur aux octets inversés, puis vous pouvez appeler ToArray ().
Erik Philips

1
Reverse () est la méthode d'extension de IEnumerable. Le code ci-dessus est parfaitement correct.
Ant

3
Cette méthode donne des nombres négatifs pour certaines adresses IP (par exemple, 140.117.0.0)
lightxx

7

Comme personne n'a posté le code qui utilise BitConverteret vérifie réellement l'endianness, voici:

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

et retour:

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

2
Vous devez utiliser un uint, mais c'est la réponse la plus correcte ici.
RubberDuck

1
@RubberDuck: Vous devez uniquement utiliser un uintsi vous voulez que les 32 bits de données intsoient un nombre non signé, un est capable de contenir les mêmes informations. Si vous souhaitez le stocker dans une base de données intmieux adaptée, vous avez besoin d'un bigintpour pouvoir le stocker sous la forme non signée.
Guffa

1
Un uint est une meilleure représentation de l'OMI. Un int signé a besoin d'un peu pour le signe, vous perdez donc des adresses tout en haut de gamme. Oui, il peut contenir les mêmes données, mais il sera affiché sous la forme d'un nombre négatif, qui n'est pas une adresse IP valide si vous le saisissez dans la barre d'adresse.
RubberDuck

@RubberDuck: Sous la forme d'un, uintvous ne pouvez pas non plus le taper dans la barre d'adresse, vous devez d'abord le convertir en texte pour ce faire. Le simple fait d'utiliser la forme la plus simple de transformation d'un inttexte en texte ne produit pas d'adresse IP fonctionnelle n'est pas un bon argument pour ne pas l'utiliser.
Guffa

@RubberDuck: Ce n'est pas un uint, c'est la représentation textuelle d'un uint.
Guffa

7

J'ai rencontré des problèmes avec les solutions décrites, face à des adresses IP avec une très grande valeur. Le résultat serait que l'octet [0] * 16777216 thingy déborderait et deviendrait une valeur int négative. ce qui l'a résolu pour moi, c'est une opération de moulage de type 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;
}

me semble être la meilleure solution mais je changerais 1 ligne, je ferais byte [] bytes = ip.MapToIPv4 (). GetAddressBytes ();
Walter Vehoeven

4

L'inverse de la fonction 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;
}

pouvez-vous expliquer plus à ce sujet?
Developerium

3

Ma question était close, je ne sais pas pourquoi. La réponse acceptée ici n'est pas la même que ce dont j'ai besoin.

Cela me donne la valeur entière correcte pour une adresse 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;
    }
}

Tant que vous pouvez effectuer la conversion dans les deux sens, je ne vois pas pourquoi le numéro de sortie doit être correct tant qu'il est cohérent.
GateKiller

Dépend de ce pour quoi vous souhaitez utiliser le numéro. Vous ne pouvez pas utiliser l'autre conversion pour faire une requête> = et <= pour trouver une adresse IP ..
Coolcoder

2

Avec l'UInt32 dans le bon format petit-boutiste, voici deux fonctions de conversion 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();
}

2

Assemblage de plusieurs des réponses ci-dessus dans une méthode d'extension qui gère l'endianness de la machine et gère les adresses IPv4 qui ont été mappées à 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");
    }
}

Tests unitaires:

[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

Si vous étiez intéressé par la fonction, pas seulement la réponse, voici comment cela se fait:

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

avec à firsttravers fourthles segments de l'adresse IPv4.


1
Je pense que ce serait plus clair si vous aviez utilisé shift au lieu de Math.Pow.
Mehrdad Afshari

Cela lèvera une exception de débordement si first est> 127. La réponse de Davy Landman est la meilleure façon de le faire.
mhenry1384

int32 est signé donc il retournera une valeur négative> 127 pour le premier octet
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'exemple ci-dessus serait la façon dont je vais .. La seule chose que vous pourriez avoir à faire est de convertir en UInt32 à des fins d'affichage, ou à des fins de chaîne, y compris en l'utilisant comme une longue adresse sous forme de chaîne.

C'est ce qui est nécessaire lors de l'utilisation de la fonction IPAddress.Parse (String). Soupir.


0

voici une solution que j'ai élaborée aujourd'hui (j'aurais dû googler d'abord!):

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

J'utilise LINQ, lambda et certaines des extensions sur les génériques, donc bien qu'il produise le même résultat, il utilise certaines des nouvelles fonctionnalités du langage et vous pouvez le faire en trois lignes de code.

j'ai l'explication sur mon blog si vous êtes intéressé.

bravo, -jc


0

Je pense que c'est faux: "65536" ==> 0.0.255.255 "Devrait être:" 65535 "==> 0.0.255.255" ou "65536" ==> 0.1.0.0 "


0

@Davy Ladman votre solution avec shift est correcte mais seulement pour ip commençant par un nombre inférieur ou égal à 99, en fait le premier octect doit être converti en long.

Quoi qu'il en soit, la reconversion avec le type long est assez difficile car stocker 64 bits (pas 32 pour Ip) et remplir 4 octets avec des zéros

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

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

Prendre plaisir!

Massimo


0

En supposant que vous ayez une adresse IP au format chaîne (par exemple 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);

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

la méthode GetAddressBytes peut renvoyer les octets dans l'ordre inverse, selon que la machine est endian ou endian-ness donc l'instruction correcte peut être pour certaines machines: long m_Address = (address [0] << 24 | address [1] << 16 | adresse [2] << 8 | adresse [3]) & 0x0FFFFFFFF
MiguelSlv

0

J'ai remarqué que System.Net.IPAddress a la propriété Address (System.Int64) et le constructeur, qui acceptent également le type de données Int64. Vous pouvez donc l'utiliser pour convertir l'adresse IP vers / depuis le format numérique (mais pas Int32, mais Int64).


-1

Jetez un œil à certains des exemples d'analyse fous dans 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


Ce n'est pas vraiment une réponse. Pourquoi ne pas le mettre en commentaire sur l'une des principales réponses?
DanM7
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.