Bagaimana cara mengubah alamat IPv4 menjadi integer di C #?

99

Saya mencari fungsi yang akan mengubah alamat IPv4 standar menjadi Integer. Poin bonus tersedia untuk fungsi yang melakukan kebalikannya.

Solusi harus dalam C #.

GateKiller
sumber
15
PERINGATAN : bilangan bulat yang dihasilkan dari jawaban yang diterima akan salah , karena alamat IP berada dalam urutan jaringan (big-endian), sedangkan ints adalah little-endian pada kebanyakan sistem. Jadi, Anda harus membalik byte sebelum mengonversi. Lihat jawaban saya untuk konversi yang benar. Juga, bahkan untuk IPv4, an inttidak dapat menyimpan alamat yang lebih besar dari 127.255.255.255, misalnya alamat broadcast, jadi gunakan a uint.
Saeb Amini

Jawaban:

137

Integer 32-bit unsigned adalah alamat IPv4. Sementara itu, IPAddress.Addressproperti, meski tidak digunakan lagi, adalah Int64 yang mengembalikan nilai 32-bit unsigned dari alamat IPv4 (tangkapannya adalah, dalam urutan byte jaringan, jadi Anda perlu menukarnya).

Misalnya, google.com lokal saya ada di 64.233.187.99. Itu setara dengan:

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

Dan memang, http: // 1089059683 / berfungsi seperti yang diharapkan (setidaknya di Windows, diuji dengan IE, Firefox dan Chrome; meskipun tidak berfungsi di iPhone).

Berikut adalah program uji untuk menunjukkan kedua konversi, termasuk pertukaran byte jaringan / 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));
    }
}
Barry Kelly
sumber
2
Dikonfirmasi di Opera 11 di Ubuntu Linux 10.04: itu mengubah int kembali ke bentuk wxyz yang sudah dikenal, dan Berfungsi.
Piskvor meninggalkan gedung
6
IPAddress.Address sudah usang sejak .Net 4.0.
Erik Philips
@ErikPhilips Apakah itu penting jika Anda hanya menggunakan IPv4?
Ray
Itu keputusan arsitektur. Ada pro dan kontra, dan komentar bukanlah tempat terbaik untuk membahas masalah khusus itu.
Erik Philips
1
Anda dapat menggunakan BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () untuk memperbaiki IPAddress.Address sudah usang
Mhmd
43

Berikut adalah sepasang metode untuk mengonversi dari IPv4 ke bilangan bulat yang benar dan sebaliknya:

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

Contoh

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

Penjelasan

Alamat IP berada dalam urutan jaringan (big-endian), sedangkan ints adalah little-endian pada Windows, jadi untuk mendapatkan nilai yang benar, Anda harus membalikkan byte sebelum mengonversi pada sistem little-endian.

Juga, bahkan untuk IPv4, an inttidak dapat menyimpan alamat yang lebih besar dari 127.255.255.255, misalnya alamat broadcast (255.255.255.255), jadi gunakan a uint.

Saeb Amini
sumber
Ini tampaknya tidak membuat perbedaan pada Windows yang menggunakan .NET - tidak yakin tentang Mono. Lihat dotnetfiddle.net/cBIOj2
Jesse
2
@ Jesse kebetulan tidak membuat perbedaan untuk masukan Anda 1.1.1.1karena array byte-nya adalah palindromik. Coba dengan yang non-palindromik seperti 127.0.0.1atau 192.168.1.1.
Saeb Amini
Saya melihat masalahnya. Nomor berulang seperti 1.1.1.1, 2.2.2.2, 123.123.123.123selalu menghasilkan hasil yang sama. Untuk anak cucu, lihat biola yang diperbarui: dotnetfiddle.net/aR6fhc
Jesse
Ingin mencatat bahwa saya harus menggunakan System.Net.IPAddressagar ini berfungsi. Bekerja dengan baik!
Shrout1
1
@Jesse itu tidak hanya untuk mengulang angka, tapi untuk semua alamat IP palindromic . Begitu pula 2.1.1.2halnya.
Saeb Amini
40

@Barry Kelly dan @Andrew Hare, sebenarnya, menurut saya mengalikan adalah cara paling jelas untuk melakukan ini (semuanya benar).

Alamat IP "diformat" Int32 dapat dilihat sebagai struktur berikut

[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

Jadi untuk mengubah alamat ip 64.233.187.99 Anda bisa melakukan:

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

jadi Anda bisa menambahkannya menggunakan + atau Anda bisa binairy atau bersama-sama. Menghasilkan 0x40E9BB63 yaitu 1089059683. (Menurut pendapat saya, mencari di hex jauh lebih mudah untuk melihat byte)

Jadi Anda bisa menulis fungsinya sebagai:

int ipToInt(int first, int second, 
    int third, int fourth)
{
    return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
Davy Landman
sumber
1
Saya percaya bahwa ip dispesifikasikan sedemikian rupa untuk secara khusus memungkinkan perilaku seperti itu ... pergeseran bit jauh lebih efisien pada kebanyakan mikroprosesor daripada muls dan add.
Ape-inago
1
@ Perkalian kera-inago dengan kekuatan konstan dua biasanya dioptimalkan ke dalam pergeseran bit, fwiw.
Barry Kelly
Alih-alih menggeser bit, Anda dapat menggunakan LayoutKind.Explicitdan FieldOffsetmembalik urutan penyimpanan byte. Tentu saja, itu hanya berfungsi untuk arsitektur little endian. Contoh di github .
RubberDuck
2
Harus menunjukkan yang intditandatangani sehingga jika Anda menggeser 192 dengan 24 bit Anda akan mendapatkan bilangan bulat negatif sehingga kode ini rusak untuk oktet tinggi yang memiliki bit tinggi di tempat pertama.
Mateusz
12

Coba yang ini:

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();
}
Rubens Farias
sumber
Reverse()pengembalian tidak berlaku, jadi Anda tidak dapat memanggilnya ToArray()(untuk pembaca mendatang). Sebagai gantinya, tetapkan nilai ke byte yang dibalik, lalu Anda dapat memanggil ToArray ().
Erik Philips
1
Reverse () adalah metode ekstensi IEnumerable. Kode di atas sangat oke.
Semut
3
Metode ini menghasilkan angka negatif untuk IP tertentu (misalnya 140.117.0.0)
lightxx
7

Karena tidak ada yang memposting kode yang menggunakan BitConverterdan benar-benar memeriksa endianness, ini dia:

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

dan kembali:

byte[] ip = BitConverter.GetBytes(num);
if (BitConverter.IsLittleEndian) {
  Array.Reverse(ip);
}
string address = String.Join(".", ip.Select(n => n.ToString()));
Guffa
sumber
2
Anda perlu menggunakan uint, tetapi ini adalah jawaban yang paling benar di sini.
RubberDuck
1
@RubberDuck: Anda hanya perlu menggunakan uintjika Anda ingin 32 bit data sebagai nomor tak bertanda tangan, dan intmampu menyimpan informasi yang sama. Jika Anda ingin menyimpannya dalam database dan intlebih cocok, Anda membutuhkannya bigintagar dapat menyimpannya dalam bentuk unsigned.
Guffa
1
Uint adalah representasi IMO yang lebih baik. Int yang ditandatangani membutuhkan sedikit untuk tanda, jadi Anda kehilangan alamat di bagian paling atas rentang. Ya, ini dapat menyimpan data yang sama, tetapi akan dikeluarkan sebagai angka negatif, yang bukan merupakan IP yang valid jika Anda mengetiknya di bilah alamat.
RubberDuck
@RubberDuck: Dalam bentuk dan uintAnda juga tidak dapat mengetikkannya di bilah alamat, Anda harus mengonversinya terlebih dahulu ke dalam bentuk teks untuk melakukannya. Hanya karena menggunakan bentuk paling sederhana untuk mengubah sebuah intteks tidak menghasilkan alamat IP yang berfungsi bukanlah argumen yang baik untuk tidak menggunakannya.
Guffa
@RubberDuck: Itu bukan an uint, itu representasi teks dari sebuah uint.
Guffa
7

Saya mengalami beberapa masalah dengan solusi yang dijelaskan, saat menghadapi Alamat IP dengan nilai yang sangat besar. Hasilnya adalah, bahwa thingy byte [0] * 16777216 akan meluap dan menjadi nilai int negatif. apa yang memperbaikinya untuk saya, adalah operasi pengecoran tipe sederhana.

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;
}
Qiong Wu
sumber
menurut saya sebagai solusi terbaik namun saya akan mengubah 1 baris, saya akan melakukan byte [] bytes = ip.MapToIPv4 (). GetAddressBytes ();
Walter Vehoeven
4

Kebalikan dari fungsi 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;
}
Aris Schizas
sumber
bisakah Anda menjelaskan lebih lanjut tentang ini?
Developerium
3

Pertanyaan saya ditutup, saya tidak tahu mengapa. Jawaban yang diterima di sini tidak sama dengan yang saya butuhkan.

Ini memberi saya nilai integer yang benar untuk sebuah 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;
    }
}
Coolcoder
sumber
Selama Anda dapat melakukan konversi dengan kedua cara tersebut, saya tidak mengerti mengapa nomor keluaran harus benar asalkan konsisten.
GateKiller
Tergantung pada apa Anda ingin menggunakan nomor tersebut. Anda tidak dapat menggunakan konversi lain untuk melakukan kueri> = dan <= untuk menemukan IP ..
Coolcoder
2

Dengan UInt32 dalam format little-endian yang tepat, berikut dua fungsi konversi sederhana:

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

Mengumpulkan beberapa jawaban di atas menjadi metode ekstensi yang menangani Ketahanan mesin dan menangani alamat IPv4 yang dipetakan ke 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");
    }
}

Tes unit:

[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);
    }
}
Doug Clutter
sumber
1

Jika Anda tertarik dengan fungsinya, bukan hanya jawabannya di sini adalah cara melakukannya:

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

dengan firstmelalui fourthmenjadi segmen dari alamat IPv4.

Andrew Hare
sumber
1
Saya pikir akan lebih jelas jika Anda menggunakan shift daripada Math.Pow.
Mehrdad Afshari
Ini akan memunculkan pengecualian overflow jika yang pertama adalah> 127. Jawaban Davy Landman adalah cara terbaik untuk melakukan ini.
mhenry1384
int32 ditandatangani sehingga akan mengembalikan nilai negatif> 127 untuk oktet pertama
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;
}
xadd
sumber
1
    public static Int32 getLongIPAddress(string ipAddress)
    {
        return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0));
    }

Contoh di atas akan menjadi cara saya pergi .. Satu-satunya hal yang mungkin harus Anda lakukan adalah mengubah ke UInt32 untuk tujuan tampilan, atau tujuan string termasuk menggunakannya sebagai alamat panjang dalam bentuk string.

Yang diperlukan saat menggunakan fungsi IPAddress.Parse (String). Mendesah.

Gerard ONeill
sumber
0

inilah solusi yang saya kerjakan hari ini (seharusnya sudah googling dulu!):

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

Saya menggunakan LINQ, lambda dan beberapa ekstensi pada generik, jadi meskipun menghasilkan hasil yang sama, ia menggunakan beberapa fitur bahasa baru dan Anda dapat melakukannya dalam tiga baris kode.

Saya punya penjelasan di blog saya jika Anda tertarik.

tepuk tangan, -jc


sumber
0

Saya pikir ini salah: "65536" ==> 0.0.255.255 "Seharusnya:" 65535 "==> 0.0.255.255" atau "65536" ==> 0.1.0.0 "


sumber
0

@Davy Ladman solusi Anda dengan shift sudah benar tetapi hanya untuk ip yang dimulai dengan angka kurang atau sama dengan 99, sebenarnya oktek pertama harus dibuang sampai panjang.

Pokoknya mengkonversi kembali dengan tipe lama cukup sulit karena simpan 64 bit (bukan 32 untuk Ip) dan isi 4 byte dengan nol

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

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

Nikmati!

Massimo

Massimo
sumber
0

Dengan asumsi Anda memiliki Alamat IP dalam format string (mis. 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));
Andrew
sumber
0
var ipAddress = "10.101.5.56";

var longAddress = long.Parse(string.Join("", ipAddress.Split('.').Select(x => x.PadLeft(3, '0'))));

Console.WriteLine(longAddress);

Keluaran: 10101005056

xagyg
sumber
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);
shzy2012
sumber
metode GetAddressBytes dapat mengembalikan byte dalam urutan terbalik, tergantung apakah mesin tersebut endian atau endian-ness sehingga pernyataan yang benar mungkin untuk beberapa mesin: long m_Address = (address [0] << 24 | address [1] << 16 | alamat [2] << 8 | alamat [3]) & 0x0FFFFFFFF
MiguelSlv
0

Saya perhatikan bahwa System.Net.IPAddress memiliki properti Alamat (System.Int64) dan konstruktor, yang juga menerima tipe data Int64. Jadi Anda dapat menggunakan ini untuk mengonversi alamat IP ke / dari format numerik (meskipun bukan Int32, tetapi Int64).

Ondřej
sumber
-1

Lihatlah beberapa contoh parsing gila di .Net's IPAddress.Parse: ( 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

abelenky
sumber
Ini bukanlah jawaban. Mengapa tidak meletakkannya sebagai komentar di salah satu jawaban teratas?
DanM7