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 #.
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 #.
Risposte:
Gli interi senza segno a 32 bit sono indirizzi IPv4. Nel frattempo, la IPAddress.Address
proprietà, 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));
}
}
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 int
s 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 int
non può contenere indirizzi più grandi 127.255.255.255
, ad esempio (255.255.255.255)
, dell'indirizzo di trasmissione , quindi usa a uint
.
1.1.1.1
perché il suo array di byte è palindromico. Prova con quelli non palindromici come 127.0.0.1
o 192.168.1.1
.
1.1.1.1
, 2.2.2.2
, 123.123.123.123
producono sempre lo stesso risultato. Per i posteri, guarda il violino aggiornato: dotnetfiddle.net/aR6fhc
System.Net.IPAddress
per farlo funzionare. Funziona alla grande!
2.1.1.2
sarebbe lo stesso.
@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);
}
LayoutKind.Explicit
e FieldOffset
per invertire l'ordine in cui vengono memorizzati i byte. Ovviamente funziona solo per l'architettura Little Endian. Esempio su GitHub .
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.
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 ().
IEnumerable
. Il codice sopra è perfettamente corretto.
Poiché nessuno ha pubblicato il codice che utilizza BitConverter
e 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()));
uint
se 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 bigint
per poterlo memorizzare nella forma non firmata.
uint
non è 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 int
in testo non produce un indirizzo IP funzionante non è un buon argomento per non usarlo.
uint
, è la rappresentazione testuale di un file uint
.
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;
}
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;
}
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;
}
}
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();
}
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);
}
}
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 first
attraverso fourth
i segmenti dell'indirizzo 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'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.
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
Penso che sia sbagliato: "65536" ==> 0.0.255.255 "Dovrebbe essere:" 65535 "==> 0.0.255.255" o "65536" ==> 0.1.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
var address = IPAddress.Parse("10.0.11.174").GetAddressBytes();
long m_Address = ((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
int
s 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, unint
non può contenere indirizzi più grandi127.255.255.255
, ad esempio , dell'indirizzo di trasmissione, quindi usa unuint
.