内置.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());
答案 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);
}
}