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 #.
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 #.
Respostas:
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));
}
}
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 int
s 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 IPv4
an int
nã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
.
1.1.1.1
porque seu array de bytes é palíndromo. Experimente com os não palíndrômicos como 127.0.0.1
ou 192.168.1.1
.
1.1.1.1
, 2.2.2.2
, 123.123.123.123
sempre produzir o mesmo resultado. Para a posteridade, veja o violino atualizado: dotnetfiddle.net/aR6fhc
System.Net.IPAddress
para fazer isso funcionar. Funciona bem!
2.1.1.2
seria o mesmo.
@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);
}
LayoutKind.Explicit
e FieldOffset
para inverter a ordem em que os bytes são armazenados. Claro, isso só funciona para a arquitetura little endian. Exemplo no github .
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.
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();
}
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 ().
IEnumerable
. O código acima está perfeitamente ok.
Como ninguém postou o código que usa BitConverter
e 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()));
uint
se quiser os 32 bits de dados como um número sem sinal e int
for capaz de conter as mesmas informações. Se você quiser armazená-lo em um banco de dados e int
for mais adequado, você precisa bigint
ser capaz de armazená-lo na forma não assinada.
uint
você 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 int
em texto não produz um endereço IP funcional, não é um bom argumento para não usá-lo.
uint
, é a representação de texto de um uint
.
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;
}
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;
}
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;
}
}
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();
}
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);
}
}
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 first
através fourth
sendo os segmentos do endereço 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));
}
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.
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
Acho que isso está errado: "65536" ==> 0.0.255.255 "Deveria ser:" 65535 "==> 0.0.255.255" ou "65536" ==> 0.1.0.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
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 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, umint
não pode conter endereços maiores que127.255.255.255
, por exemplo , o endereço de broadcast, então use auint
.