IPv4 映射的 IPv6 地址

本文关键字:地址 IPv6 映射 IPv4 | 更新日期: 2023-09-27 18:31:19

我正在尝试制作一个与IP版本无关的客户端/服务器。我一直在C++中玩这个,并想出了一些使用 IN6ADDR_SETV4MAPPED 宏工作的东西(正如Microsoft如此善意地推荐的那样)。我按照此处的代码示例完成了此操作;我转换地址的代码与示例没有什么不同,一切正常。我可以通过键入 IPv4 和 IPv6 地址从客户端连接到服务器(应用程序相应地进行映射)。

现在我正在寻找 C# 解决方案来升级我制作的简单聊天服务器,但我一直找不到有关如何使用映射地址的任何资源。我还没有找到在 .net 中提供等效IN6ADDR_SETV4MAPPED或任何其他工具的函数。我的问题是:如何在 C#(客户端)中使用 IPv4 映射的 IPv6 地址

我尝试过:

  1. 字符串"::ffff:"添加到点分 IPv4 表示法,使用此地址调用Socket.Connect。生成的地址字符串类似于 ::ffff:127.0.0.1
  2. 前置字符串"::ffff:" 。将每个八位字节从点状格式转换为十六进制并用冒号分隔,调用Socket.Connect。生成的地址字符串类似于 ::ffff:7f:0:0:1

到目前为止,这两种方法都没有奏效。

服务器的代码片段:

this.m_endPoint = new IPEndPoint(IPAddress.IPv6Any, 1337);
this.m_server.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
this.m_server.Bind(this.m_endPoint);
this.m_server.Listen(10);

客户端的代码片段:

public ClientNet(string host, short port)
{
    IPAddress ip;
    if(IPAddress.TryParse(host, out ip))
    {
        string[] octs = host.Split(new char[] { '.' });
        host = "::ffff:";
        for(int i = 0; i < octs.Length; ++i)
        {
            host += string.Format("{0:x}", int.Parse(octs[i]));
            if(i + 1 != octs.Length)
            {
                host += ":";
            }
        }
    }
    else
    {
        throw new ClientCreateException("[in ClientNet.Constructor] Unable to create client; use IPv4 address");
    }
    Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
    client.Connect(host, port);
    . . . //More initialization
}

IPv4 映射的 IPv6 地址

今天回到这个问题,想着我也许能弄清楚。我做到了!答案很简单,我觉得自己像个傻瓜,一年没有得到它。

关于我发布的代码的两件事:

  1. 应该使用IPAddress.MaptoIPv6(请参阅 MSDN 链接)而不是我编写的更容易出错的愚蠢、人为的循环。

    一个。后来我在 .NET 4.0 中工作时意识到,我在示例中使用的便利函数在 .NET 4.5 之前不可用。我拼凑的一个快速代码示例位于本文的底部,以防其他人卡在早期版本的 .NET 中。

  2. 真正的解决方案:需要在呼叫client.Connect()之前呼叫client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);

下面是我今天编写的示例应用程序的完整代码示例,用于测试它。我能够使用 ::1 和 127.0.0.1 作为地址建立连接。请注意,服务器Socket是为 IPv6 创建的,并且客户端和服务器上的 SocketOptionName.IPv6Only 选项都设置为 0。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace sixsharp
{
    class Program
    {
        static void Main(string[] args)
        {
            if(args.Length <= 0) //run as server
                RunServer();
            else
                RunClient(args);
            Console.WriteLine("Press enter to close.");
            Console.ReadLine();
        }
        static void RunServer()
        {
            using(Socket serv = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
            {
                serv.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
                serv.Bind(new IPEndPoint(IPAddress.IPv6Any, 1337));
                serv.Listen(5);
                Console.Write("Listening for client connection...");
                using(Socket client = serv.Accept())
                {
                    Console.WriteLine("Client connection accepted from {0}", client.RemoteEndPoint.ToString());
                    byte[] buf = new byte[128];
                    client.Receive(buf, 128, SocketFlags.None);
                    Console.WriteLine("Got ''{0}'' from client", Encoding.ASCII.GetString(buf));
                    Console.WriteLine("Echoing response");
                    client.Send(buf);
                    client.Shutdown(SocketShutdown.Both);
                }
            }
            Console.WriteLine("Done.");
        }
        static void RunClient(string[] args)
        {
            using(Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
            {
                client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
                Console.WriteLine("Setting up address, input is {0}", args[0]);
                IPEndPoint ep;
                try
                {
                    ep = new IPEndPoint(IPAddress.Parse(args[0]), 1337);
                }
                catch(FormatException fe)
                {
                    Console.WriteLine("IP address was improperly formatted and not parsed.");
                    Console.WriteLine("Detail: {0}", fe.Message);
                    return;
                }
                if(ep.AddressFamily == AddressFamily.InterNetwork)
                {
                    ep = new IPEndPoint(ep.Address.MapToIPv6(), ep.Port);
                    if(!ep.Address.IsIPv4MappedToIPv6 || ep.Address.AddressFamily != AddressFamily.InterNetworkV6)
                    {
                        Console.WriteLine("Error mapping IPv4 address to IPv6");
                        return;
                    }
                }
                Console.WriteLine("Connecting to server {0} ...", ep.ToString());
                try
                {
                    client.Connect(ep);
                }
                catch(Exception ex)
                {
                    Console.WriteLine("Unable to connect.'n Detail: {0}", ex.Message);
                    return;
                }
                client.Send(Encoding.ASCII.GetBytes("This is a test message. Hello!"));
                byte[] buf = new byte[128];
                client.Receive(buf);
                Console.WriteLine("Got back from server: {0}", Encoding.ASCII.GetString(buf));
                client.Shutdown(SocketShutdown.Both);
            }
            Console.WriteLine("Done.");
        }
    }
}

客户端输出:

设置地址,输入为 10.2.6.179
连接到服务器 [::ffff:10.2.6.179]:1337 ...
从服务器返回:这是一条测试消息。你好!

做。
按回车键关闭。

服务器输出:

正在侦听客户端连接...接受来自 [::ffff:10.2.6.179]:56275
的客户端连接 得到"这是一条测试消息。你好!
' 来自客户端
回声响应
做。
按回车键关闭。

提供早期版本的 .NET 中缺少的便利函数的示例扩展方法:

static class IPAddressExtensions
{
    public static IPAddress MapToIPv6(this IPAddress addr)
    {
        if(addr.AddressFamily != AddressFamily.InterNetwork)
            throw new ArgumentException("Must pass an IPv4 address to MapToIPv6");
        string ipv4str = addr.ToString();
        return IPAddress.Parse("::ffff:" + ipv4str);
    }
    public static bool IsIPv4MappedToIPv6(this IPAddress addr)
    {
        bool pass1 = addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6, pass2;
        try
        {
            pass2 = (addr.ToString().StartsWith("0000:0000:0000:0000:0000:ffff:") ||
                    addr.ToString().StartsWith("0:0:0:0:0:ffff:") ||
                    addr.ToString().StartsWith("::ffff:")) && 
                    IPAddress.Parse(addr.ToString().Substring(addr.ToString().LastIndexOf(":") + 1)).AddressFamily == AddressFamily.InterNetwork;
        }
        catch
        {
            return false;
        }
        return pass1 && pass2;
    }
}