How do I do TCP hole punching?

…衆ロ難τιáo~ 提交于 2019-12-02 18:34:31

I'd use the "sequential hole punching technique" detailed in http://www.bford.info/pub/net/p2pnat/index.html. It seems much simpler to do that simultaneous connections and socket reuse. It is not necessary for hole punching to do anything exactly simultaneously (that is a meaningless notion in distributed systems anyway).

I have implemented hole punching. My router seems not to like it. Wireshark shows the outbound hole punching SYN is correct but the remote party can't get through to me. I verifies all ports with TcpView.exe and disabled all firewalls. Must be a router issue. (It is a strange and invasive router.)

class HolePunchingTest
{
    IPEndPoint localEndPoint;
    IPEndPoint remoteEndPoint;
    bool useParallelAlgorithm;

    public static void Run()
    {
        var ipHostEntry = Dns.GetHostEntry("REMOTE_HOST");

        new HolePunchingTest()
        {
            localEndPoint = new IPEndPoint(IPAddress.Parse("LOCAL_IP"), 1234),
            remoteEndPoint = new IPEndPoint(ipHostEntry.AddressList.First().Address, 1235),
            useParallelAlgorithm = true,
        }.RunImpl();
    }

    void RunImpl()
    {
        if (useParallelAlgorithm)
        {
            Parallel.Invoke(() =>
            {
                while (true)
                {
                    PunchHole();
                }
            },
            () => RunServer());
        }
        else
        {

            PunchHole();

            RunServer();
        }
    }

    void PunchHole()
    {
        Console.WriteLine("Punching hole...");

        using (var punchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
        {
            EnableReuseAddress(punchSocket);

            punchSocket.Bind(localEndPoint);
            try
            {
                punchSocket.Connect(remoteEndPoint);
                Debug.Assert(false);
            }
            catch (SocketException socketException)
            {
                Console.WriteLine("Punching hole: " + socketException.SocketErrorCode);
                Debug.Assert(socketException.SocketErrorCode == SocketError.TimedOut || socketException.SocketErrorCode == SocketError.ConnectionRefused);
            }
        }

        Console.WriteLine("Hole punch completed.");
    }

    void RunServer()
    {
        using (var listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
        {
            EnableReuseAddress(listeningSocket);

            listeningSocket.Bind(localEndPoint);
            listeningSocket.Listen(0);

            while (true)
            {
                var connectionSocket = listeningSocket.Accept();
                Task.Run(() => ProcessConnection(connectionSocket));
            }
        }
    }

    void ProcessConnection(Socket connectionSocket)
    {
        Console.WriteLine("Socket accepted.");

        using (connectionSocket)
        {
            connectionSocket.Shutdown(SocketShutdown.Both);
        }

        Console.WriteLine("Socket shut down.");
    }

    void EnableReuseAddress(Socket socket)
    {
        if (useParallelAlgorithm)
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    }
}

You can try both values for useParallelAlgorithm. Both should work.

This code is for the server. It punches a hole into the local NAT. You can then connect from the remote side using any client that allows to pick the local port. I used curl.exe. Apparently, telnet on Windows does not support binding to a port. wget apparently neither.

Verify that the ports are correct on both sides using TcpView or Process Explorer. You can use Wireshark to verify packets. Set a filter like tcp.port = 1234.

When you "call out" to punch a hole you enable the tuple (your-ip, your-port, remote-ip, remote-port) to communicate. This means that all further communication must use those values. All sockets (inbound or outbound) must use these exact port numbers. In case you aren't aware: outgoing connections can control the local port as well. This is just uncommon.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!