2011-02-01 24 views
5

Phương thức builtNet .Net System.Net.IPAddress.ToString() hoạt động không nhất quán đối với địa chỉ IPv6.Điều gì quyết định định dạng địa chỉ IPv6 của System.Net.IPAddress.ToString()?

Cho mảng byte 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, trong một số môi trường, "aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa" được trả về, trong khi các giá trị khác trả về "aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:172.172.172.172".

Tôi hiểu rằng cả hai đều là định dạng IPv6 hợp lệ, nhưng tôi muốn có thể giải thích sự khác biệt.

Dường như môi trường mới hơn (Windows 7 và Server 2008 R2) có nhiều khả năng tạo ra hành vi đầu tiên, vì vậy tôi đã kiểm tra những khác biệt rõ ràng như phiên bản .Net framework, nhưng tôi không thể phát hiện mẫu.

Có cách nào tôi có thể chọn một định dạng trên định dạng khác hay không, tôi có phải viết mã này để có được hành vi nhất quán không?

Mã để tái tạo:

byte[] bytes = {0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA}; 
    IPAddress myIP = new IPAddress(bytes); 
    Console.WriteLine(myIP.ToString()); 

Trả lời

4

Poking xung quanh trong ruột của ToString, sử dụng Reflector, bạn có thể thấy rằng nếu nó xác định rằng hệ điều hành hỗ trợ IPv6 (đối với một số giá trị của sự hỗ trợ), sau đó nó trì hoãn đến một hàm Win32 gọi WSAAddressToString, trong khi nếu lá cờ này không được thiết lập, nó định dạng thủ công (từ đó là mảng byte nội bộ) như:

addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[0] })).Append(':'); 
addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[1] })).Append(':'); 
addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[2] })).Append(':'); 
addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[3] })).Append(':'); 
addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[4] })).Append(':'); 
addressString.Append(string.Format(CultureInfo.InvariantCulture, "{0:x4}", new object[] { this.m_Numbers[5] })).Append(':'); 
addressString.Append((int) ((this.m_Numbers[6] >> 8) & 0xff)).Append('.'); 
addressString.Append((int) (this.m_Numbers[6] & 0xff)).Append('.'); 
addressString.Append((int) ((this.m_Numbers[7] >> 8) & 0xff)).Append('.'); 
addressString.Append((int) (this.m_Numbers[7] & 0xff)); 

mà sẽ luôn luôn trở lại với định dạng thứ hai bạn đã thể hiện.

Cờ cờ "OS hỗ trợ IPv6" có được đặt phụ thuộc vào cả kiến ​​thức nội bộ trong lớp (phiên bản phải> 2000) và nó sẽ xuất hiện một nỗ lực thực sự để tạo ổ cắm IPv6 - vì vậy nếu bạn bật Máy XP bị tắt IPv6, tôi nghĩ bạn cũng sẽ nhận được định dạng thứ hai này.

1

Đối với một số trường hợp, nó không phụ thuộc vào hệ điều hành hỗ trợ cờ IPv6. Tôi nhận được vấn đề này trên windows server 2008 R2.I cố gắng này

String ipString = "2400:3C00:3FFE:0000:0000:5EFE:8999:48AA"; 
    System.Net.IPAddress address; 
    IPAddress.TryParse(ipString, out address); 

nhưng address.ToString() được trả lại giá trị "2400: 3c00: 3ffe :: 5efe: 137.153.72.170".

Nhưng nếu tôi thay đổi chuỗi IP thành "2400: 3C00: 3FFE: 1000: 1000: 5EFE: 8999: 48AA" hoạt động của nó đúng cách.

0

Trong trường hợp của tôi, tôi cần đảm bảo định dạng nhất quán mà không cần đến một cuộc gọi API không được quản lý đến WSAAddressToString, vì vậy tôi đã viết phương thức mở rộng sau. Có thể điều này sẽ giúp một người nào đó trong tương lai:

/// <summary> 
/// Returns the IPv4 or IPv6 address in standard notation. Any transitional suffix (i.e. an IPv4-like address 
/// displayed in place of the final two segments of an IPv6 address) returned by .NET is converted to standard colon notation. 
/// See http://stackoverflow.com/questions/4863352/what-dictates-the-formatting-of-ipv6-addresses-by-system-net-ipaddress-tostring. 
/// </summary> 
public static string ToStringNonTransitional(this System.Net.IPAddress oIPAddress) 
{ 
    var sIP = oIPAddress.ToString(); 

    if (oIPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 
     return sIP; // Return IPv4 addresses untouched. 

    if (oIPAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6) 
     throw new Exception(string.Format("Can't handle '{0}' in '{1}' format. (Only IPv4 or IPv6 supported.)", sIP, oIPAddress.AddressFamily.ToString())); 

    if (!sIP.Contains(".")) 
     return sIP; 

    try 
    { 
     var iTransitionalStart = sIP.LastIndexOf(":") + 1; 
     var sTransitionalPart = sIP.Substring(iTransitionalStart); 
     sIP = sIP.Substring(0, iTransitionalStart); 
     var asOctects = sTransitionalPart.Split('.'); 
     sIP += string.Format("{0:x2}{1:x2}", Convert.ToInt16(asOctects[0]), Convert.ToInt16(asOctects[1])).TrimStart('0'); 
     sIP += ":" + string.Format("{0:x2}{1:x2}", Convert.ToInt16(asOctects[2]), Convert.ToInt16(asOctects[3])).TrimStart('0'); 

     return sIP; 
    } 
    catch (Exception ex) 
    { 
     throw new Exception("Failed to convert IPv6 address to standard notation: " + sIP, ex); 
    } 
}