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 #.
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 #.
Réponses:
Les entiers non signés 32 bits sont des adresses IPv4. Pendant ce temps, la IPAddress.Address
proprié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));
}
}
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 int
s 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 int
ne 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
.
1.1.1.1
car son tableau d'octets est palindromique. Essayez avec des non-palindromiques comme 127.0.0.1
ou 192.168.1.1
.
1.1.1.1
, 2.2.2.2
, 123.123.123.123
donnent toujours le même résultat. Pour la postérité, voir le violon mis à jour: dotnetfiddle.net/aR6fhc
System.Net.IPAddress
pour que cela fonctionne. Fonctionne très bien!
2.1.1.2
serait pareil.
@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);
}
LayoutKind.Explicit
et FieldOffset
pour 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 .
int
c'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.
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 ().
IEnumerable
. Le code ci-dessus est parfaitement correct.
Comme personne n'a posté le code qui utilise BitConverter
et 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()));
uint
si vous voulez que les 32 bits de données int
soient un nombre non signé, un est capable de contenir les mêmes informations. Si vous souhaitez le stocker dans une base de données int
mieux adaptée, vous avez besoin d'un bigint
pour pouvoir le stocker sous la forme non signée.
uint
vous 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 int
texte en texte ne produit pas d'adresse IP fonctionnelle n'est pas un bon argument pour ne pas l'utiliser.
uint
, c'est la représentation textuelle d'un uint
.
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;
}
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;
}
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;
}
}
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();
}
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);
}
}
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 à first
travers fourth
les segments de l'adresse 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));
}
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.
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
Je pense que c'est faux: "65536" ==> 0.0.255.255 "Devrait être:" 65535 "==> 0.0.255.255" ou "65536" ==> 0.1.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
var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
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
int
s 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, unint
ne peut pas contenir d'adresses plus grandes que127.255.255.255
, par exemple, l'adresse de diffusion, alors utilisez unuint
.