从主机名解析 IPv6 地址

Resolve IPv6 address from hostname

我们正在更新套接字代码以处理新的 iOS 要求,您需要能够在纯 IPv6 环境中进行连接。

我们目前正在使用 .NET 套接字和 TcpClient。

我正在尝试建立一个 IPv6 和 IPv4 都可以工作的解决方案,但到目前为止什么都没有发生。

如果我使用:

TcpClient client = new TcpClient();
client.BeginConnect( "server.hostname.com", 8182, this._onConnect, client );

在IPv6环境下,我没有收到任何回应; _onConnect 方法永远不会被触发。

如果我使用:

TcpClient client = new TcpClient( AddressFamily.InterNetworkV6 );
client.BeginConnect( "server.hostname.com", 8182,this._onConnect, client);

我得到 SocketException:

An address incompatible with the requested protocol was used (errorCode: 10047, socketErrorCode: AddressFamilyNotSupported, nativeErrorCode: 10047)

这很烦人,如果可以理解的话。如果我使用:

TcpClient client = new TcpClient( AddressFamily.InterNetworkV6 );
client.BeginConnect( "xxxx:xxxx:xxxx::xxxx:xxxx", 8182,this._onConnect, client);

然后我可以连接,但是:

所以我想我可以从主机名中获取支持的 IP 并尝试一个接一个地连接。

使用此代码:

IPHostEntry hostInfo = Dns.GetHostEntry( "server.hostname.com" );
foreach(IPAddress ip in hostInfo.AddressList)
    Debug.Log( "Ip: " + ip.ToString() + ": address family: " + ip.AddressFamily );

它只会打印出 IPv4 IP,这不是很有用。

我在这里做错了什么?如何从字符串主机名获取 IPv6 IP?或者有更好的方法来解决这个问题吗?

我假设这会在 DNS 级别自动解析,但我猜 Unity 中包含的 Mono 版本不够新。

谢谢

看起来在单声道中,通过 主机名 来创建或连接到服务器将失败。使用直接 IP 始终有效。 Unity 已经很久没有更新 mono socket 的东西了。使用IP没有错。

他们错过了 1 号的 Apple 截止日期。我对您的建议是首先检查您的应用程序是否在 iOS 上 运行,然后对所有内容使用 IPv6。如果 IPv6 失败或 returns null 则使用 IPv4。

It'll only print out the IPv4 IPs, which isn't super helpful.

What am I doing wrong here? How can I get the IPv6 IP from a string hostname? Or is there a better way to resolve this?

它打印出 IPv4,因为您 not 过滤了它。您必须在每个循环中手动检查 ip 是否为 IPv6。

//IPv4
if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
}

//IPv6
if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
}

如果使用上述解决方案过滤后仍未找到 IPv6,则意味着您所在的网络 not 支持IPv6。当我这么说时,我指的是您的 ISP。如果您的 ISP 没有为您提供 IPv6,那么您将无法使用 IPv6 连接到另一个 public 服务器。您将只能使用您的 ISP 分配给您的 IPv4 连接到 public 服务器。

要了解这一点,请访问 here and here。它会告诉您是否有 IPv4IPv6 或两者。

下面的代码是我用来检索本地的通用函数,public IP 包括版本 4 和 6。

private string getIPAddress(string hostName, IPTYPE ipType, ADDRESSFAM Addfam)
    {
        //Return null if ADDRESSFAM is Ipv6 but Os does not support it
        if (Addfam == ADDRESSFAM.IPv6 && !System.Net.Sockets.Socket.OSSupportsIPv6)
        {
            return null;
        }

        //////////////HANDLE LOCAL IP(IPv4 and IPv6)//////////////
        if (ipType == IPTYPE.LOCAL_IP)
        {

            System.Net.IPHostEntry host;
            string localIP = "";
            host = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
            foreach (System.Net.IPAddress ip in host.AddressList)
            {
                //IPv4
                if (Addfam == ADDRESSFAM.IPv4)
                {
                    if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        localIP = ip.ToString();
                    }
                }

                 //IPv6
                else if (Addfam == ADDRESSFAM.IPv6)
                {
                    if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                    {
                        localIP = ip.ToString();
                    }
                }
            }
            return localIP;

        }

        //////////////HANDLE PUBLIC IP(IPv4 and IPv6)//////////////
        if (ipType == IPTYPE.PUBLIC_IP)
        {
            //Return if hostName String is null
            if (string.IsNullOrEmpty(hostName))
            {
                return null;
            }

            System.Net.IPHostEntry host;
            string localIP = "";
            host = System.Net.Dns.GetHostEntry(hostName);
            foreach (System.Net.IPAddress ip in host.AddressList)
            {
                //IPv4
                if (Addfam == ADDRESSFAM.IPv4)
                {
                    if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        localIP = ip.ToString();
                    }
                }

                //IPv6
                else if (Addfam == ADDRESSFAM.IPv6)
                {
                    if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                    {
                        localIP = ip.ToString();
                    }
                }

            }
            return localIP;
        }
        return null;
    }


    enum IPTYPE
    {
        LOCAL_IP, PUBLIC_IP
    }

    enum ADDRESSFAM
    {
        IPv4, IPv6
    }

用法:

    Debug.Log("Local IPv4: " + getIPAddress("", IPTYPE.LOCAL_IP, ADDRESSFAM.IPv4));
    Debug.Log("Local IPv6: " + getIPAddress("", IPTYPE.LOCAL_IP, ADDRESSFAM.IPv6));

    Debug.Log("Public IPv4: " + getIPAddress("google.com", IPTYPE.PUBLIC_IP, ADDRESSFAM.IPv4));
    Debug.Log("Public IPv6: " + getIPAddress("google.com", IPTYPE.PUBLIC_IP, ADDRESSFAM.IPv6));

@程序员的代码给出了主机名的 IP 地址,但我想澄清两件事。

1) 代码:

TcpClient client = new TcpClient( AddressFamily.InterNetworkV6 );
client.BeginConnect( "server.hostname.com", 8182,this._onConnect, client);

有效,但仅适用于 iOS。在编辑器和 Android 上,它给出了一个异常(从 Unity 5.3.5f1 开始)。这似乎是实现中的错误,所以我发送了错误报告。

2) 尽管 System.Net.Sockets.Socket.SupportsIPv6 被标记为过时并且 Unity 建议您使用 System.Net.Sockets.Socket.OSSupportsIPv6,但 OSSupportsIPv6 将在 Android

上抛出异常