打孔后未建立 TCP 连接

Posted

技术标签:

【中文标题】打孔后未建立 TCP 连接【英文标题】:TCP connection not being made after holepunching 【发布时间】:2015-05-25 07:00:28 【问题描述】:

所以我正在尝试在两个移动设备(android 手机和带有 USB 移动宽带调制解调器的笔记本电脑)之间实现 TCP 打孔。我使用另一台连接到 wifi 的笔记本电脑作为服务器。

我首先让笔记本电脑连接到显示其公共 IP 地址和服务器看到的 NAT 端口号的服务器。

 try
        System.out.println("connect to server");

        Socket connectToServer = new Socket();
        System.out.println("49");
        connectToServer.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
        System.out.println("51");
        connectToServer.connect(new InetSocketAddress(serverIPAddress.getHostAddress(), serverPort));
        System.out.println("connected to server: " + connectToServer);
        connectToServer.close();

    
    catch (Exception e)
        System.out.println("Exception 1 :" + e.toString());
        //connectToServer.close();
    

手机做同样的事情,所以我看到它是公共 IP 和 NAT 的端口号。 笔记本电脑和手机都关闭了与服务器的连接。

然后笔记本电脑使用相同的本地 IP 和端口号(用于连接到服务器)并使用移动设备的公共 IP 和经过 NAT 的端口号连接到移动设备。

    try
        Socket punchSocket = new Socket();
        System.out.println("33");
        punchSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
        System.out.println("35");
        punchSocket.connect(new InetSocketAddress(mobileAddress.getHostAddress(), mobilePort));
        punchSocket.close();


    
    catch(Exception e)
        System.out.println("exception 2 caught " + e.toString());

    

由于手机在nat后面,连接超时,出现异常。

try
                Socket mobileSocket = new Socket();
                System.out.println("105");
                mobileSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
                System.out.println("124");
                mobileSocket.connect(new InetSocketAddress(mobileAddress.getHostAddress(), mobilePort));
                System.out.println("connection made: " + mobileSocket);
                mobileSocket.close();


            
            catch(Exception e)
                System.out.println("exception 2 caught " + e.toString());

            

最后,我尝试再次连接到手机。当笔记本电脑尝试连接到手机时,我也从手机发起连接。使用相同的 IP 和端口(用于连接服务器的手机),我连接到笔记本电脑的公共 IP 和端口。

但手机和笔记本电脑都尝试连接只是超时。

我不仅尝试第二次从笔记本电脑连接到手机,还尝试让笔记本电脑监听连接:

        ServerSocket welcomeSocket = new ServerSocket(myPort, 50, myIPAddress);

        welcomeSocket.setSoTimeout(5);
        System.out.println("Start listening for mobile to connect");


        while(true)
        try
            Socket newsock = welcomeSocket.accept();
            System.out.println("connection made: " + newsock);

            
        catch(Exception e) 
            System.out.println("exception 4 caught " + e.toString());

           
         

但这也不起作用。我不知道为什么。有没有办法让我测试我的移动提供商的 NAT 是否允许打孔?

这是我从手机连接的方式:

try 
        Log.d("stop", "line 64");
        InetAddress serverIPAddress = InetAddress.getByName(serverIP);
        InetAddress myIPAddress = InetAddress.getByName(myIP); //InetAddress.getLocalHost(); 
        Log.d("stop", "line");
        Log.d("localHost: ", myIPAddress.getHostAddress());
        Socket clientSocket = new Socket();
        Log.d("stop", "line 70");
        clientSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
        Log.d("stop", "line 72");
        clientSocket.connect(new InetSocketAddress(serverIPAddress.getHostAddress(), serverPort));
        Log.d("stop", "line 74");

        BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
        receivedMessage = inFromClient.readLine();
        Log.d("stop", "line 78");


        clientSocket.close();
        //Toast.makeText(this, "socket connected", Toast.LENGTH_SHORT).show();

        return receivedMessage; //receivedMessage;
     catch (IOException e) 

        //Toast.makeText(this, "socket caughyt", Toast.LENGTH_SHORT).show();
        e.printStackTrace();
        error1 = "no Server";
        Log.d("error", error1 + e.getMessage());
    

我今天大部分时间都在试图解决这个问题。在笔记本电脑上使用 serversocket 时,移动设备上的错误是超时。昨晚(凌晨 1 点)它随机工作了一次,我就去睡觉了。今天又不上班了。 不知道为什么不。 任何帮助将不胜感激。

编辑:

好的,现在在笔记本电脑尝试连接到移动设备但失败(然后不断尝试连接到移动设备而不是服务器套接字场景)之后,移动设备会尝试连接到笔记本电脑并通过。如果我删除这些行: BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); receivedMessage = inFromClient.readLine(); 然后手机说它已连接。如果我把线路留在里面,手机会收到对等方重置连接的错误。

但是,无论哪种情况,笔记本电脑似乎都没有响应。如果我使用服务器套接字替代方案而不是尝试从笔记本电脑连接,则由于连接超时,移动设备尝试连接失败。

【问题讨论】:

更新:花了我一段时间,但我意识到为我的手机和 USB 调制解调器提供 IP 地址的 NAT 具有多个公共 IP,因此这两个设备实际上位于同一个 NAT 和同一个专用网络中。所以明天我要去取另一个运营商的 SIM 卡,看看打孔是否真的有效。 【参考方案1】:

看起来您有一个很好的解决方案来查找两个设备的正确公共地址。然而,即使有了这些信息,许多(甚至可能是大多数)移动网络仍然不允许传入 TCP 连接到移动设备。

有些设备对不同的设备类型有不同的限制,这可能就是您看到它有时可以与移动宽带调制解调器一起使用但从不与手机一起使用的原因。这些差异甚至可能仅仅是因为在运营商网络中调制解调器和电话连接的设置不同(例如,使用不同的 APN 和不同的规则)。

您很可能知道,但您仍然可以使用服务器作为中间的“中继”点进行端到端通信 - 这显然会为您的解决方案增加服务器端的复杂性,但它具有在大多数情况下可靠工作的优势运营商。该方法类似于:

笔记本电脑连接到服务器 手机连接到服务器 笔记本电脑向服务器询问已连接设备的列表并“发现”移动设备 笔记本电脑向移动设备发送消息。它实际上将消息发送到服务器,并指示将其发送到移动设备上。 服务器通知移动设备有消息可用(例如使用某种消息通知,例如 Google Cloud Messaging,或者只是通过设备定期轮询以查看它们是否有任何消息)。 手机从服务器检索消息。

【讨论】:

您好,感谢您的回复。所以现在我可以始终如一地从手机连接到笔记本电脑。每次它都会在移动端连接。但笔记本电脑端将无法识别这一点。有没有办法在尝试从同一端口连接时打开服务器套接字?如果笔记本电脑未尝试连接,我看不到手机如何连接到笔记本电脑的 natted IP 和端口。我在 USB 调制解调器中使用手机 sim。使用与我手机上相同的 APN 会有帮助吗?

以上是关于打孔后未建立 TCP 连接的主要内容,如果未能解决你的问题,请参考以下文章

Java TCP 打孔

无法使用 STUN 执行 TCP 打孔

UDP打孔混乱

我因 python TCP 打孔而被拒绝连接

简述TCP协议建立连接的过程

UDP 打孔 - 无法到达目的地