我该怎么做TCP打孔?

问题描述 投票:9回答:1

问题是下面。这是我目前的测试代码没有成功。

static void Main(string[] args)
{
    if (args.Count() != 3)
    {
        Console.WriteLine("Bad args");
    }
    var ep = new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1]));
    var lp = new IPEndPoint(IPAddress.Any, int.Parse(args[2]));

    var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    s.Bind(lp);

    var c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    c.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    c.Bind(lp);

    Task.Run(() => { try { c.Connect(ep); } catch { } });
    s.Listen(10);
    var v = s.Accept();
    v.Close();
}

我该怎么做TCP打孔?我使用的是远程服务器测试。我跑wget local_public_ip:port/test。我有我的80端口路由器设置,因此不需要打孔。我的代码有一个连接。现在,我尝试在其他端口,我不能确切地弄清楚如何冲洞。

我所做的是(C#代码)

var l = new TcpListener(8090);
l.Start();
try { var o = new TcpClient(); o.Connect("myserverip", 123); }
catch(Exception ex) {}
var e = l.AcceptSocket();
Console.WriteLine(e.RemoteEndPoint.AddressFamily);

我想,也许我需要设置了TCP连接上本地端点。

TcpClient(new System.Net.IPEndPoint(new System.Net.IPAddress(bytearray), port));

我犯了一个错误,并得到了这个异常

The requested address is not valid in its context

修复了字节数组192,168,1,5它似乎正确地进行呼出所连接。现在,我有一个输出连接使用我想我的监听端口的远程IP的wget将能够连接到我。这是不是这样的

我该怎么做TCP打孔?

sockets tcp hole-punching
1个回答
11
投票

我会用“连续打洞技术”,在http://www.bford.info/pub/net/p2pnat/index.html详细说明。这似乎简单得多这样做同时连接插座重用。对于打孔恰好同时(即在分布式系统中一个无意义的概念,反正)做任何事情,这是没有必要的。

我已经实现打孔。我的路由器似乎不喜欢它。 Wireshark的显示出打孔SYN是正确的,但对方也打不通给我。我验证与TcpView.exe和禁用所有的防火墙的所有端口。必须是路由器的问题。 (这是一个奇怪和侵入路由器。)

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);
    }
}

你可以尝试useParallelAlgorithm两个值。双方应该工作。

此代码是服务器。它的冲头的孔进入本地NAT。然后,您可以从远程端使用的任何客户端,允许选择本地端口连接。我用curl.exe。显然,在Windows Telnet不支持绑定到一个端口。 wget的显然都不是。

验证端口上使用两面套装软件或进程资源管理器都正确。您可以使用Wireshark来验证数据包。设置像tcp.port = 1234的过滤器。

当你“咻”地打眼启用元组(您-IP,你的端口,远程IP,远程端口)进行通信。这意味着,所有进一步的沟通必须使用这些值。所有插座(入站或出站)必须使用这些精确的端口号。如果你不知道:传出连接可以控制本地端口为好。这仅仅是少见。

© www.soinside.com 2019 - 2024. All rights reserved.