1

我已经在 C# 中实现了 TCP 打孔。我得到的是,客户端正在相互发送 SYN 消息,但它们被丢弃在对面的路由器上。场景是这样的:Client:A--------Router:A--------Server--------Router:B--------Client :B

如果客户端:A 向客户端:B 发送 SYN 数据包,它会在路由器:B 被丢弃,这是明显的 NAT 行为。但是,根据打孔场景,现在如果客户端:B 向客户端:A 发送 SYN,它应该通过路由器:A 的 NAT。在我的例子中, router:A 也丢弃了这个数据包。在 netstat 中,它将与 B 的连接显示为 SYN_SENT,几秒钟后,我收到一条消息,例如“客户端没有响应。连接失败”

根据我在这方面的研究,放弃的原因可能是无声放弃或主动拒绝。如果是静默丢弃,则应打洞,B 的 SYN 应通过 A 的 NAT。如果是主动拒绝,路由器:A 将收到路由器:B 关于丢弃的通知消息,A 将关闭洞。

问题是我在 A 没有收到任何通知消息。(通过路由器日志检查)。我还尝试了一个从 https://github.com/jasonpang/tcp-holepunching获取的已经实现的 TCP 孔 pucnhing 示例

但我得到了类似的结果。帮助我找出解决方案。我也怀疑路由器打孔的超时。

命名空间 HolePunching { 公共类 HolePunchingClient { 私有 Socket _clientSocket;

    private Socket _serverSocket;
    private Socket _connectSocket;
    //private Socket _serverSocket;

    private Socket _holePunchedSocket;

    public HolePunchingClient (IPEndPoint localEndPoint)
    {
        /*For the moment, only TCP Hole Punching is supported*/
        _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        _clientSocket.Bind(localEndPoint);

        //_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        //_serverSocket.Bind(localEndPoint);
    }

    private void ProcessClientRequest (object sender, DoWorkEventArgs eventArgs)
    {
        try
        {
            IPEndPoint iep = (IPEndPoint)eventArgs.Argument;
            _connectSocket.Connect(iep);
            Console.WriteLine("Connected correctly with: " + iep);
            _serverSocket.Close();
            _holePunchedSocket = _connectSocket;
            Console.WriteLine("\n\n\nException check clientRequest\n\n\n");
        }
        catch (Exception e)
        {
            Console.WriteLine("In ProcessClientRequest: " + e.Message);
            return;
        }
    }

    private void ProcessServerRequest (object sender, DoWorkEventArgs eventArgs)
    {
        try
        {
            _serverSocket.Listen(10);
            Socket s = _serverSocket.Accept();
            Console.WriteLine("Received connection from: " + s.RemoteEndPoint);
            _holePunchedSocket = s;
            Console.WriteLine("\n\n\nException check serverRequest\n\n\n");
        }
        catch (Exception e)
        {
            Console.WriteLine("In ProcessServerRequest: " + e.Message);
            return;
        }
    }

    private void ProcessRequest (object sender, DoWorkEventArgs eventArgs)
    {
        while (true)
        {
            byte[] bytes = new byte[2048];
            _clientSocket.Receive(bytes);

            MessageType messageType = (MessageType) bytes[0];
            Console.WriteLine("MessageType received: " + messageType);

            switch (messageType)
            {
                case MessageType.ConnectClient:
                    byte[] byteAddress = new byte[4];
                    byte[] bytePort = new byte[2];
                    Buffer.BlockCopy(bytes, 1, byteAddress, 0, 4);
                    Buffer.BlockCopy(bytes, 5, bytePort, 0, 2);
                    IPEndPoint remoteEndPoint = new IPEndPoint(new IPAddress(byteAddress), BitConverter.ToUInt16(bytePort, 0));
                    Console.WriteLine("HP will be done towards this address: " + remoteEndPoint);

                    _connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _connectSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    _connectSocket.Bind(_clientSocket.LocalEndPoint);

                    _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    _serverSocket.Bind(_clientSocket.LocalEndPoint);

                    BackgroundWorker bwClient = new BackgroundWorker();
                    bwClient.DoWork += ProcessClientRequest;

                    BackgroundWorker bwServer = new BackgroundWorker();
                    bwServer.DoWork += ProcessServerRequest;

                    bwClient.RunWorkerAsync(remoteEndPoint);
                    bwServer.RunWorkerAsync();

                    break;
            }
        }
    }

    public void Connect (IPEndPoint serverEndPoint)
    {
        _clientSocket.Connect(serverEndPoint);
        _clientSocket.Send(BitConverter.GetBytes((byte)MessageType.Register));

        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += ProcessRequest;
        bw.RunWorkerAsync();
    }

    public Socket HolePunch (IPAddress otherAddress)
    {
        byte[] bytes = new byte[5];
        Buffer.BlockCopy(BitConverter.GetBytes((byte)MessageType.RequestClient), 0, bytes, 0, 1);
        Buffer.BlockCopy(otherAddress.GetAddressBytes(), 0, bytes, 1, 4);

        _clientSocket.Send(bytes);

        while (_holePunchedSocket == null)
        {
            System.Threading.Thread.Sleep(1500);
        }

        return _holePunchedSocket;
    }
}

}

这是我从上面的链接中获取的客户端代码。我在连接到客户端 B/A 时收到异常消息“其他客户端没有响应。连接失败”。

客户端 A 和 B 都运行该程序的相同副本。他们中的任何一个都会向服务器发送请求以与另一个客户端连接。服务器将向两者发送 IP-Port 组合。并且客户端代码将通过相互发送连接请求来继续。(最终超时)

4

0 回答 0