IPv4映射的IPv6地址

时间:2013-09-13 21:17:10

标签: c# .net sockets ip

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

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

我尝试了什么:

  1. 将字符串"::ffff:"添加到点分IPv4表示法,使用此地址调用Socket.Connect。结果地址字符串看起来像::ffff:127.0.0.1
  2. 前置字符串"::ffff:"。将每个八位字节从点分格式转换为十六进制并用冒号分隔,调用Socket.Connect。结果地址字符串看起来像::ffff:7f:0:0:1
  3. 到目前为止,这些方法都没有奏效。

    服务器的代码段:

    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
    }
    

1 个答案:

答案 0 :(得分:3)

今天回到这里,以为我可能能够弄明白。我做到了!答案非常简单,我觉得因为一年没法得到这个问题就像个傻瓜。

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

  1. 应该使用IPAddress.MaptoIPv6 (see MSDN link)而不是我写的那种愚蠢的,有人造的循环,它更容易出错。

    一个。后来我在.NET 4.0中工作时意识到,在我的样本中使用的便利功能在.NET 4.5之前是不可用的。我把它放在一起的快速代码示例位于本文的底部,以防其他人陷入早期版本的.NET。

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

  3. 这是我今天编写的示例应用程序的完整代码示例,用于测试它。我能够使用:: 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 ...
      从服务器回来:这是一条测试消息。你好!

         

    完成。
      按enter键关闭。

    服务器输出:

      

    收听客户端连接...从[::ffff:10.2.6.179]:566275接受客户端连接   得到'这是一条测试信息。您好!
                       '来自客户
      回应响应
      完成。
      按Enter键关闭。

    示例扩展方法在早期版本的.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;
        }
    }