是什么规定了System.Net.IPAddress.ToString()的IPv6地址格式?

时间:2011-02-01 13:48:10

标签: .net ip-address ipv6

内置.Net方法System.Net.IPAddress.ToString()对IPv6地址的行为不一致。

给定字节数组0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,在某些环境中返回"aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa",而其他环境返回"aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:172.172.172.172"

据我所知,两者都是有效的IPv6格式,但我希望能够解释其中的差异。

似乎较新的环境(Windows 7和Server 2008 R2)更有可能产生第一种行为,因此我检查了明显的差异,例如.Net框架版本,但我一直无法检测到模式。 / p>

有没有办法可以选择一种格式而不是另一种格式,或者我是否需要对此进行编码以获得一致的行为?

要重新创建的代码:

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

3 个答案:

答案 0 :(得分:5)

使用Reflector在ToString的内部进行探讨,您可以看到,如果确定操作系统支持IPv6(对于某些支持值),那么它将推迟到名为{{3}的Win32函数如果没有设置这个标志,它会手动格式化(从它的内部字节数组):

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

将始终返回您显示的第二种格式。

标志“OS支持IPv6”标志是否设置似乎取决于类中的内部知识(版本必须> 2000)并且它似乎是创建IPv6套接字的实际尝试 - 所以如果你在禁用IPv6的XP机器,我想你也会得到第二种格式。

答案 1 :(得分:1)

对于某些情况,它不依赖于OS支持IPv6标志。我在Windows Server 2008 R2上遇到此问题。我试过这个

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

但是address.ToString()返回值“2400:3c00:3ffe :: 5efe:137.153.72.170”。

但如果我将IP字符串更改为“2400:3C00:3FFE:1000:1000:5EFE:8999:48AA”,它的工作正常。

答案 2 :(得分:0)

在我的情况下,我需要确保一致的格式化,而不需要对WSAAddressToString进行非托管API调用,因此我编写了以下扩展方法。也许这将有助于未来的人:

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