Làm cách nào để chuyển đổi địa chỉ IPv4 thành số nguyên trong C #?


99

Tôi đang tìm kiếm một hàm sẽ chuyển đổi địa chỉ IPv4 chuẩn thành Số nguyên. Điểm thưởng có sẵn cho một chức năng sẽ làm điều ngược lại.

Giải pháp phải ở trong C #.


15
CẢNH BÁO : các số nguyên dẫn đến từ câu trả lời được chấp nhận sẽ sai vì địa chỉ IP theo thứ tự mạng (big-endian), trong khi ints là little-endian trên hầu hết các hệ thống. Vì vậy, bạn phải đảo ngược các byte trước khi chuyển đổi. Xem câu trả lời của tôi để biết các chuyển đổi chính xác. Ngoài ra, ngay cả đối với IPv4, một intkhông thể chứa các địa chỉ lớn hơn 127.255.255.255, ví dụ như địa chỉ quảng bá, vì vậy hãy sử dụng a uint.
Saeb Amini

Câu trả lời:


137

Số nguyên không dấu 32-bit địa chỉ IPv4. Trong khi đó, thuộc IPAddress.Addresstính, mặc dù không được dùng nữa, là một Int64 trả về giá trị 32-bit chưa được đánh dấu của địa chỉ IPv4 (bắt là, nó theo thứ tự byte mạng, vì vậy bạn cần phải hoán đổi nó).

Ví dụ: google.com địa phương của tôi tại 64.233.187.99. Điều đó tương đương với:

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

Và thực sự, http: // 1089059683 / hoạt động như mong đợi (ít nhất là trong Windows, được thử nghiệm với IE, Firefox và Chrome; mặc dù không hoạt động trên iPhone).

Đây là một chương trình thử nghiệm để hiển thị cả hai chuyển đổi, bao gồm hoán đổi byte mạng / máy chủ:

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
Được xác nhận trong Opera 11 trên Ubuntu Linux 10.04: nó chuyển đổi int trở lại dạng wxyz quen thuộc và Nó hoạt động.
Piskvor rời tòa nhà

6
IPAddress.Address đã lỗi thời kể từ .Net 4.0.
Erik Philips

@ErikPhilips Có vấn đề gì không nếu bạn chỉ sử dụng IPv4?
Ray

Đó là một quyết định kiến ​​trúc. Nó có ưu và nhược điểm, và bình luận không phải là nơi tốt nhất để thảo luận về vấn đề cụ thể đó.
Erik Philips

1
bạn có thể sử dụng BitConverter.ToUInt32 (IPAddress.Parse (value) .GetAddressBytes (). Reverse (). ToArray () để sửa IPAddress .Address đã lỗi thời
.Address

43

Dưới đây là một số phương pháp để chuyển đổi từ IPv4 thành một số nguyên chính xác và quay lại:

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

Thí dụ

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

Giải trình

Địa chỉ IP theo thứ tự mạng (big-endian), trong khi ints là little-endian trên Windows, vì vậy để nhận được giá trị chính xác, bạn phải đảo ngược các byte trước khi chuyển đổi trên hệ thống little-endian.

Ngoài ra, ngay cả đối với IPv4, mộtint không thể chứa các địa chỉ lớn hơn 127.255.255.255, ví dụ địa chỉ quảng bá (255.255.255.255), vì vậy hãy sử dụng a uint.


Điều này dường như không thực sự tạo ra sự khác biệt trên Windows sử dụng .NET - không chắc về Mono. Xem dotnetfiddle.net/cBIOj2
Jesse

2
@Jesse nó sẽ không tạo ra sự khác biệt cho đầu vào của bạn 1.1.1.1vì mảng byte của nó là palindromic. Hãy thử với những cái không phải palindromic như 127.0.0.1hoặc 192.168.1.1.
Saeb Amini

Tôi thấy vấn đề. Số lặp đi lặp lại như 1.1.1.1, 2.2.2.2, 123.123.123.123luôn mang lại kết quả tương tự. Đối với hậu thế, hãy xem cập nhật fiddle: dotnetfiddle.net/aR6fhc
Jesse

Cũng giống như lưu ý rằng tôi đã phải sử dụng System.Net.IPAddressđể làm cho nó hoạt động. Hoạt động tuyệt vời!
Shrout1

1
@Jesse sẽ không chỉ dành cho các số lặp lại, mà dành cho tất cả các địa chỉ IP palindromic . Vì vậy, 2.1.1.2sẽ giống nhau.
Saeb Amini

40

@Barry Kelly và @Andrew Hare, thực ra, tôi không nghĩ rằng nhân là cách rõ ràng nhất để làm điều này (tất cả đều đúng).

Địa chỉ IP "được định dạng" Int32 có thể được xem như cấu trúc sau

[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

Vì vậy, để chuyển đổi địa chỉ ip 64.233.187.99 bạn có thể làm:

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

vì vậy bạn có thể thêm chúng bằng cách sử dụng + hoặc bạn có thể binairy hoặc chúng cùng nhau. Kết quả là 0x40E9BB63 là 1089059683. (Theo ý kiến ​​của tôi khi nhìn bằng hệ lục phân thì việc xem các byte sẽ dễ dàng hơn nhiều)

Vì vậy, bạn có thể viết hàm dưới dạng:

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

1
Tôi tin rằng ip đã được chỉ định theo cách đó để cho phép cụ thể hành vi như vậy ... dịch chuyển bit hiệu quả hơn nhiều trên hầu hết các bộ vi xử lý so với muls và thêm vào.
Ape-inago

1
@ Các phép nhân Ape-inago với lũy thừa không đổi của hai thường được tối ưu hóa thành các dịch chuyển bit, fwiw.
Barry Kelly,

Thay vì dịch chuyển bit, bạn có thể sử dụng LayoutKind.ExplicitFieldOffsetđảo ngược thứ tự lưu trữ các byte. Tất nhiên, điều đó chỉ phù hợp với kiến ​​trúc thời kỳ cuối. Ví dụ trên github .
RubberDuck

2
Phải chỉ ra rằng nó intđược ký, vì vậy nếu bạn thay đổi 192 bằng 24 bit, bạn sẽ nhận được số nguyên âm vì vậy mã này bị hỏng đối với octet cao có bit cao ở vị trí đầu tiên.
Mateusz

12

Hãy thử những cái này:

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()trả về void, do đó bạn không thể gọi ToArray()nó (đối với những người đọc trong tương lai). Thay vào đó, hãy gán một giá trị cho các byte được đảo ngược, sau đó bạn có thể gọi ToArray ().
Erik Philips

1
Reverse () là phương thức mở rộng của IEnumerable. Mã trên là hoàn toàn ok.
Ant

3
Phương pháp này mang lại số âm cho IP nhất định (ví dụ như 140.117.0.0)
lightxx

7

Vì không ai đăng mã sử dụng BitConvertervà thực sự kiểm tra độ bền, đây là:

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

và quay lại:

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

2
Bạn cần sử dụng uint, nhưng đây là câu trả lời chính xác nhất ở đây.
RubberDuck

1
@RubberDuck: Bạn chỉ cần sử dụng một uintnếu bạn muốn 32 bit dữ liệu là một số không dấu, một intcó thể chứa cùng một thông tin. Nếu bạn muốn lưu trữ nó trong một cơ sở dữ liệu, một intphù hợp hơn, bạn cần một bigintđể có thể lưu trữ nó ở dạng không dấu.
Guffa

1
Một gợi ý là một IMO đại diện tốt hơn. Một int đã ký cần một chút cho dấu, vì vậy bạn sẽ mất các địa chỉ ở đầu dải ô. Có, nó có thể chứa cùng một dữ liệu, nhưng nó sẽ được xuất ra dưới dạng số âm, đây không phải là IP hợp lệ nếu bạn nhập nó vào thanh địa chỉ.
RubberDuck

@RubberDuck: Ở dạng uintbạn cũng không thể nhập nó vào thanh địa chỉ, trước tiên bạn phải chuyển nó thành dạng văn bản để làm điều đó. Chỉ vì sử dụng hình thức đơn giản nhất để chuyển một intvăn bản thành văn bản không tạo ra địa chỉ IP hoạt động không phải là một lý lẽ tốt để không sử dụng nó.
Guffa

@RubberDuck: Đó không phải là một uint, đó là đại diện văn bản của một uint.
Guffa

7

Tôi đã gặp một số vấn đề với các giải pháp được mô tả, khi đối mặt với Địa chỉ IP có giá trị rất lớn. Kết quả sẽ là byte [0] * 16777216 thingy sẽ tràn và trở thành một giá trị int âm. điều gì đã sửa nó cho tôi, là một thao tác đúc kiểu đơn giản.

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

với tôi đây là giải pháp tốt nhất tuy nhiên tôi sẽ thay đổi 1 dòng, tôi sẽ thực hiện byte [] bytes = ip.MapToIPv4 (). getAddressBytes ();
Walter Vehoeven

4

Mặt trái của chức năng 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;
}

bạn có thể giải thích thêm về điều này?
Developerium

3

Câu hỏi của tôi đã được đóng lại, tôi không biết tại sao. Câu trả lời được chấp nhận ở đây không giống với những gì tôi cần.

Điều này cung cấp cho tôi giá trị số nguyên chính xác cho một 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;
    }
}

Miễn là bạn có thể thực hiện chuyển đổi theo cả hai cách, tôi không hiểu tại sao số đầu ra phải chính xác miễn là nó nhất quán.
GateKiller

Phụ thuộc vào những gì bạn muốn sử dụng số cho. Bạn không thể sử dụng các chuyển đổi khác để làm a> = và <= truy vấn để tìm một IP ..
Coolcoder

2

Với UInt32 ở định dạng little-endian thích hợp, đây là hai hàm chuyển đổi đơn giản:

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

Tập hợp một số câu trả lời ở trên vào một phương thức mở rộng để xử lý Endianness của máy và xử lý các địa chỉ IPv4 đã được ánh xạ tới 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");
    }
}

Bài kiểm tra đơn vị:

[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

Nếu bạn quan tâm đến chức năng, không chỉ câu trả lời ở đây là cách nó được thực hiện:

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

với firstqua fourthlà các phân đoạn của địa chỉ IPv4.


1
Tôi nghĩ sẽ rõ ràng hơn nếu bạn sử dụng shift thay vì Math.
Mehrdad Afshari

Điều này sẽ tạo ra một ngoại lệ tràn nếu đầu tiên là> 127. Câu trả lời của Davy Landman là cách tốt nhất để làm điều này.
mhenry1384

int32 được ký kết vì vậy nó sẽ trở lại giá trị âm> 127 cho octet đầu tiên
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));
    }

Ví dụ trên sẽ là cách tôi đi .. Chỉ có điều bạn có thể phải làm là chuyển đổi sang UInt32 cho mục đích hiển thị, hoặc mục đích chuỗi bao gồm sử dụng nó như một địa chỉ dài ở dạng chuỗi.

Đó là những gì cần thiết khi sử dụng hàm IPAddress.Parse (Chuỗi). Thở dài.


0

đây là một giải pháp mà tôi đã tìm ra hôm nay (đáng lẽ phải được đưa lên Google trước!):

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

tôi đang sử dụng LINQ, lambda và một số tiện ích mở rộng trên generic, vì vậy trong khi tạo ra kết quả tương tự, nó sử dụng một số tính năng ngôn ngữ mới và bạn có thể làm điều đó trong ba dòng mã.

tôi có lời giải thích trên blog của tôi nếu bạn quan tâm.

chúc mừng, -jc


0

Tôi nghĩ điều này là sai: "65536" ==> 0.0.255.255 "Nên là:" 65535 "==> 0.0.255.255" hoặc "65536" ==> 0.1.0.0 "


0

@Davy Ladman giải pháp của bạn với shift là đúng nhưng chỉ đối với ip bắt đầu bằng số nhỏ hơn hoặc bằng 99, bát phân đầu tiên của infact phải được đúc tối đa.

Dù sao chuyển đổi lại với kiểu dài là khá khó khăn vì lưu trữ 64 bit (không phải 32 cho Ip) và điền 4 byte bằng số 0

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

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

Thưởng thức!

Massimo


0

Giả sử bạn có Địa chỉ IP ở định dạng chuỗi (ví dụ: 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);

Đầu ra: 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);

phương thức GetAddressBytes có thể trả về các byte theo thứ tự ngược lại, tùy thuộc vào máy là endian hay endian-ness nên câu lệnh đúng có thể dành cho một số máy: long m_Address = (address [0] << 24 | address [1] << 16 | địa chỉ [2] << 8 | địa chỉ [3]) & 0x0FFFFFFFF
MiguelSlv

0

Tôi nhận thấy rằng System.Net.IPAddress có thuộc tính Địa chỉ (System.Int64) và phương thức khởi tạo, cũng chấp nhận kiểu dữ liệu Int64. Vì vậy, bạn có thể sử dụng điều này để chuyển đổi địa chỉ IP sang / từ định dạng số (mặc dù không phải là Int32, mà là Int64).


-1

Hãy xem một số ví dụ phân tích cú pháp điên rồ trong Địa chỉ IP của .Net.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


Đây thực sự không phải là một câu trả lời. Tại sao không đặt nó dưới dạng nhận xét về một trong những câu trả lời hàng đầu?
DanM7
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.