Bir IPv4 adresini C #'da bir tam sayıya nasıl dönüştürebilirim?


100

Standart bir IPv4 adresini Tamsayıya dönüştürecek bir işlev arıyorum. Bunun tersini yapacak bir işlev için mevcut bonus puanlar.

Çözüm C # dilinde olmalıdır.


15
UYARI : Kabul edilen cevaptan kaynaklanan tam sayılar yanlış olacaktır , çünkü IP adresleri ağ düzenindedir (big-endian), ints çoğu sistemde küçüktür. Dolayısıyla, dönüştürmeden önce baytları tersine çevirmelisiniz. Bkz Cevabımı doğru dönüşümler için. Ayrıca, IPv4 için bile , örneğin yayın adresinden intdaha büyük adresler tutulamaz , bu nedenle a . 127.255.255.255uint
Saeb Amini

Yanıtlar:


138

32 bit işaretsiz tamsayı olan IPv4 adresleri. Bu aradaIPAddress.Address özellik, kullanımdan kaldırılmış olsa da, IPv4 adresinin işaretsiz 32 bit değerini döndüren bir Int64'tür (yakalama, ağ bayt sırasındadır, bu nedenle onu değiştirmeniz gerekir).

Örneğin, yerel google.com adresimde 64.233.187.99. Bu şuna eşdeğerdir:

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

Ve gerçekten http: // 1089059683 / beklendiği gibi çalışır (en azından Windows'ta, IE, Firefox ve Chrome ile test edilmiştir; yine de iPhone'da çalışmaz).

Ağ / ana bilgisayar bayt değişimi dahil her iki dönüşümü de gösteren bir test programı aşağıda verilmiştir:

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
Ubuntu Linux 10.04 üzerinde Opera 11'de onaylandı: int'i bilinen wxyz formuna dönüştürür ve Çalışır.
Piskvor,

6
IPAddress.Address, .Net 4.0 itibariyle kullanılmamaktadır .
Erik Philips

@ErikPhilips Yalnızca IPv4 kullanmanız önemli mi?
Ray

Bu mimari bir karar. Artıları ve eksileri vardır ve yorumlar, bu belirli konuyu tartışmak için en iyi yer değildir.
Erik Philips

1
IPAddress.Address'i düzeltmek için BitConverter.ToUInt32 (IPAddress.Parse (değer) .GetAddressBytes (). Reverse (). ToArray () kullanabilirsiniz. IPAddress.Address eski
Mhmd

43

İşte IPv4'ten doğru bir tam sayıya ve geriye doğru dönüştürmek için bir çift yöntem :

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

Misal

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

Açıklama

IP adresleri ağ düzenindedir (big-endian), ints Windows'da küçük endian iken , bu yüzden doğru bir değer elde etmek için little-endian sistemde dönüştürmeden önce baytları tersine çevirmelisiniz.

Ayrıca, IPv4bir yayın adresinden intdaha büyük adresler için bile tutulamaz , bu nedenle a kullanın .127.255.255.255(255.255.255.255)uint


Bu aslında .NET kullanan Windows'ta bir fark yaratmıyor gibi görünüyor - Mono'dan emin değil. Bkz dotnetfiddle.net/cBIOj2
Jesse

2
@Jesse 1.1.1.1, bayt dizisi palindromik olduğu için girdiniz için bir fark yaratmaz . 127.0.0.1Veya gibi palindromik olmayanları deneyin 192.168.1.1.
Saeb Amini

Sorunu görüyorum. Gibi tekrarlanan numaraları 1.1.1.1, 2.2.2.2, 123.123.123.123her zaman aynı sonucu verir. Gelecek nesil için, güncellenmiş keman sayfasına bakınız: dotnetfiddle.net/aR6fhc
Jesse

Bunu çalıştırmak için kullanmam gerektiğini not etmek isterim System.Net.IPAddress. Harika çalışıyor!
Shrout1

1
@Jesse bu sadece sayıları tekrarlamak için değil, tüm palindromik IP adresleri için. Yani 2.1.1.2aynı olacaktır.
Saeb Amini

40

@Barry Kelly ve @Andrew Hare, aslında çoğalmanın bunu yapmanın en net yolu olduğunu sanmıyorum (çok doğru).

Int32 "formatlı" bir IP adresi aşağıdaki yapı olarak görülebilir

[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

Yani 64.233.187.99 ip adresini dönüştürmek için şunları yapabilirsiniz:

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

böylece bunları + kullanarak ekleyebilir veya binairy veya birlikte kullanabilirsiniz. 1089059683 olan 0x40E9BB63 ile sonuçlanıyor. (Bence onaltılık olarak bakmak baytları görmek çok daha kolay)

Böylece işlevi şu şekilde yazabilirsiniz:

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

1
IP'lerin bu tür davranışlara özellikle izin verecek şekilde belirlendiğine inanıyorum ... bit kaymaları çoğu mikroişlemcide muls ve addlerden çok daha etkilidir.
Ape-inago

1
@ Ape-inago çarpımları ikinin sabit güçleriyle normal olarak bit kaydırmaları, fwiw olarak optimize edilir.
Barry Kelly

Bit kaydırma yerine , baytların saklandığı sırayı tersine çevirmek için LayoutKind.Explicitve kullanabilirsiniz FieldOffset. Tabii ki, bu sadece küçük endian mimarisi için çalışıyor. Github'da örnek .
RubberDuck

2
Bunun intimzalandığını belirtmek zorundasınız, böylece 192'i 24 bit kaydırırsanız, negatif tamsayı elde edersiniz, böylece bu kod, ilk etapta yüksek bit olan yüksek sekizli için kırılır.
Mateusz

12

Şunu deneyin:

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()void döndürür, bu yüzden ToArray()onu arayamazsınız (gelecekteki okuyucular için). Bunun yerine, tersine çevrilmiş baytlara bir değer atayın, ardından ToArray () 'ı çağırabilirsiniz.
Erik Philips

1
Reverse () , uzantı yöntemidir IEnumerable. Yukarıdaki kod tamamen tamam.
Ant

3
Bu yöntem belirli IP'ler için negatif sayılar verir (ör. 140.117.0.0)
lightxx

7

Hiç kimse BitConverterendianness kullanan ve gerçekten kontrol eden kodu göndermediğinden , işte:

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

ve geri:

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

2
Bir uint kullanmanız gerekir, ancak buradaki en doğru cevap budur.
RubberDuck

1
@RubberDuck: Eğer uint32 bitlik veriyi işaretsiz bir sayı olarak istiyorsanız, bir kullanmanız gerekir int, aynı bilgiyi tutabilir. Bir veritabanında depolamak istiyorsanız ve intdaha uygunsa, bigintimzasız biçimde saklayabilmeniz için a gerekir .
Guffa

1
Bir uint daha iyi bir IMO temsilidir. İşaretli bir int'in işaret için biraz ihtiyacı vardır, bu nedenle aralığın en tepesindeki adresleri kaybedersiniz. Evet, aynı veriyi tutabilir, ancak negatif bir sayı olarak çıktılanır ve bu, adres çubuğuna yazarsanız geçerli bir IP değildir.
RubberDuck

@RubberDuck: Formunu uintadres çubuğuna da yazamazsınız , bunu yapmak için önce metin formuna dönüştürmeniz gerekir. Sırf bir intmetne dönüştürmenin en basit biçimini kullanmak, çalışan bir IP adresi üretmediği için, onu kullanmamak için iyi bir argüman değildir.
Guffa

@RubberDuck: Bu bir değil uint, bir uint.
Guffa

7

Çok büyük bir değere sahip IP Adresleriyle karşılaştığımda açıklanan çözümlerde bazı sorunlarla karşılaştım. Sonuç, bayt [0] * 16777216 şeyinin taşması ve negatif bir int değerine dönüşmesi olacaktır. benim için bunu düzelten şey, basit bir tür döküm işlemi.

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

bana en iyi çözüm olarak görünüyor ancak 1 satırı değiştirirdim, bayt [] bayt = ip.MapToIPv4 () yapardım. GetAddressBytes ();
Walter Vehoeven

4

Davy Landman'ın işlevinin tersi

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

bunun hakkında daha fazla açıklayabilir misin?
Developerium

3

Sorum kapandı, neden olduğuna dair hiçbir fikrim yok. Burada kabul edilen cevap ihtiyacım olanla aynı değil.

Bu bana bir IP için doğru tamsayı değerini verir.

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

Dönüşümü her iki şekilde de yapabildiğiniz sürece, tutarlı olduğu sürece çıktı sayısının neden doğru olması gerektiğini anlamıyorum.
GateKiller

Numarayı ne için kullanmak istediğinize bağlıdır. Bir IP bulmak için> = ve <= sorgusu yapmak için diğer dönüşümü kullanamazsınız ..
Coolcoder

2

UInt32 ile uygun küçük endian biçiminde, işte iki basit dönüştürme işlevi:

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

Yukarıdaki yanıtlardan birkaçı, makinenin Endianness'ini işleyen ve IPv6'ya eşlenen IPv4 adreslerini işleyen bir uzatma yönteminde toplandı.

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

Birim testleri:

[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

Fonksiyonla ilgileniyorsanız, sadece buradaki cevap değil, nasıl yapıldığıdır:

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

ile firstaracılığıyla fourthIPv4 adres bölümleri olmak.


1
Math.Pow yerine shift kullansaydın daha net olacağını düşünüyorum.
Mehrdad Afshari

Bu, ilki> 127 ise bir taşma istisnası ortaya çıkaracaktır. Davy Landman'ın cevabı, bunu yapmanın en iyi yoludur.
mhenry1384

int32 imzalandı, bu nedenle ilk sekizli için> 127 negatif değeri döndürecektir
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));
    }

Yukarıdaki örnek benim gideceğim yol olabilir .. Yapmanız gereken tek şey, görüntüleme amacıyla veya dizge biçiminde uzun bir adres olarak kullanmak da dahil olmak üzere dize amacıyla bir UInt32'ye dönüştürmektir.

IPAddress.Parse (String) işlevini kullanırken ihtiyaç duyulan şey budur. İç çekmek.


0

İşte bugün üzerinde çalıştığım bir çözüm (önce googled olmalıydı!)

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

LINQ, lambda ve jeneriklerdeki bazı uzantıları kullanıyorum, bu yüzden aynı sonucu üretirken, bazı yeni dil özelliklerini kullanıyor ve bunu üç satır kodda yapabilirsiniz.

eğer ilgilenirseniz blogumda bir açıklama var.

şerefe, -jc


0

Bunun yanlış olduğunu düşünüyorum: "65536" ==> 0.0.255.255 "" 65535 "==> 0.0.255.255" veya "65536" ==> 0.1.0.0 "olmalıdır


0

@Davy Ladman vardiyalı çözümünüz doğru, ancak sadece 99'dan küçük veya eşit sayı ile başlayan ip için infact ilk oktekt uzun süre kullanılmalıdır.

Her neyse, uzun tiple geri dönüştürmek oldukça zordur çünkü 64 bit depolayın (Ip için 32 değil) ve 4 baytı sıfırlarla doldurun

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

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

Zevk almak!

Massimo


0

Dize biçiminde bir IP Adresiniz olduğunu varsayarsak (ör. 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);

Çıkış: 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);

GetAddressBytes yöntemi, makinenin endian veya endian olmasına bağlı olarak baytları ters sırada döndürebilir, bu nedenle doğru ifade bazı makineler için olabilir: long m_Address = (adres [0] << 24 | adres [1] << 16 | adres [2] << 8 | adres [3]) & 0x0FFFFFFFF
MiguelSlv

0

System.Net.IPAddress'in Address özelliğine (System.Int64) ve Int64 veri türünü de kabul eden yapıcıya sahip olduğunu fark ettim. Böylece, IP adresini sayısal (Int32 olmasa da Int64) biçimine dönüştürmek için bunu kullanabilirsiniz.


-1

NET'in IPAddress.Parse içindeki bazı çılgın ayrıştırma örneklerine bir göz atın: ( 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


Bu gerçekten bir cevap değil. Neden bunu en iyi cevaplardan birine yorum olarak koymuyorsunuz?
DanM7
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.