使用C#将端口X的流量转发到计算机B,“UDP打洞穿过防火墙”

8
我需要从家里的电脑与办公室的电脑建立TCP连接。在办公室里有一个路由器,连接着几台电脑。该路由器有互联网连接,因此连接到该路由器的所有电脑都可以上网。我家里有一台有互联网接入的电脑。我需要让我的办公室电脑充当服务器,而我的家庭电脑则连接到它。以前,我可以通过在服务器上端口转发流量来进行连接:
    NATUPNPLib.UPnPNATClass upnpnat;
    NATUPNPLib.IStaticPortMappingCollection mappings;

    public ServerExample()
    {
        InitializeComponent();

        upnpnat = new NATUPNPLib.UPnPNATClass();
        mappings = upnpnat.StaticPortMappingCollection;

        //                           server local IP address
        mappings.Add(1300, "TCP", 1300, "192.168.150.146", true, "plsease work");
        // this code tels the router to forward all tcp traffic comming from port
        // 1300 to the server computer (it's lan ip address happens to be 192.168.150.146)
        //...

我曾经可以从家里连接。 (我知道简单的方法是在办公室路由器上打开端口并将它们转发到我的计算机,但问题是我无法访问办公室路由器)

现在他们用新的路由器替换了我的办公室路由器,我无法使用我的代码。现在,使用新路由器时,当我执行以前的代码时,我会得到:

enter image description here

请注意,映射返回null; 因此,我无法添加映射。

我确定应该有一种建立连接的方法,因为办公室中的某些人例如使用limewire或bit torrent。 我认为我的问题可能与权限有关? 我如何解决这个问题?


编辑

通过研究,我发现我要做的是“防火墙UDP穿透”。实际上,我想通过tcp连接做到这一点。我不知道TCP和UPD puch holing之间的区别...我的意思是为了客户端能够在不必在路由器上进行配置的情况下找到一个对等体。

.

.

.

.

.

.

更新

好的,所以我相信我已经尝试使用c#对这个问题进行了发帖:好的,让我向你展示我的操作:

请注意,您可能需要参考此图以便理解我将要解释的内容: enter image description here

正如您所知,我想在计算机A和计算机B之间建立TCP连接。我设法做到这一点是通过所谓的TCP穿透。

第1步: 首先,我在服务器S上开始侦听新连接。

                   TcpListener server = new TcpListener(System.Net.IPAddress.Parse(“192.168.11.109”), 55550);
                   Server.Start();

                   var client = server.AcceptSocket();  \\ wait here until someone connects

步骤二: 现在用A电脑连接到服务器,方法如下:
          TcpClient tcpClient = new TcpClient("192.168.11.109", 55550);

第三步: 在电脑A上执行第二步代码后,服务器S的调试结果应该如下图所示: enter image description here 第四步: 现在我们的目标是从电脑B连接到电脑A。服务器S拥有B需要建立连接所需的信息。实际上,我需要在电脑B和服务器S之间建立连接,这样服务器S才能提供B所需的参数,以便B连接到A。
第五步: 由于我正在进行调试,所以我能够看到这些参数,因此我现在将电脑A设为服务器,监听端口3313。我希望电脑A现在可以在该端口(3313)上监听,因为所有发送到路由器X的使用端口3313的数据包都应该被发送到电脑A。
       \\ COMPUTER A 
       TcpListener server = new TcpListener(System.Net.IPAddress.Parse("192.168.0.120"), 3313);
        server.Start();

        var newClient = server.AcceptSocket();  \\ wait here until a client gets connected

步骤六: 现在,计算机A应该在3313端口上等待新连接。重要的是3313端口,因为路由器X应该将从该端口接收到的所有数据包转发到计算机A上。
计算机A正在等待新连接。 enter image description here 步骤七: 现在快速行动!我们希望从计算机B建立连接。实际上,服务器S将传递参数,但由于我只是尝试让这个工作起来,所以我将在计算机B上快速编写程序。
          TcpClient tcpClient = new TcpClient(“192.168.11.108”, 3313);
           \\192.168.11.108  is the address of router X

最后:

由于路由器X没有将数据包转发到计算机A,因此计算机B无法连接到计算机A。(我知道这是因为我在路由器X上启用了端口转发功能,并且当我使用该端口时它能够正常工作)我的意思是,我不明白为什么路由器X没有将从端口3313发送来的流量转发到计算机A。计算机A已经与服务器S建立了连接,所有通过端口3313向路由器X发送给服务器S的内容都已被发送到计算机A。那么,如果我通过端口3313向路由器X发送数据包,为什么计算机A不能接收到呢?

注:

请注意,我展示的所有内容实际上都涉及三个路由器X、Y和Z,以及服务器S、计算机A和计算机B:


这通常不是编码问题,而是行政/政策问题:在大多数组织中,将内部机器暴露在互联网上是不允许的。如果您仍然愿意这样做,我建议以友好的方式直接与网络管理员交谈。 - Alexei Levenkov
是的,如果我联系管理部门,我应该得到许可。只是因为我很好奇,因为我能够使用BitTorrent、LimeWire和其他P2P应用程序。通过编写代码解决这个问题会更好,而不是与管理部门交谈,而且我还可以学习 :) - Tono Nam
这些协议的工作方式不同......请搜索“UDP穿越防火墙”。 - Yahia
http://www.sectechno.com/2010/10/31/bypassing-firewalls-using-icmp-tunnel/ - patrick
4个回答

4
你的新工作路由器可能已禁用 UPnP,因此出现空引用。
没有 UPnP,您的服务器无法对入站流量进行可见,因为路由器不知道将入站数据包发送到哪里。在这种情况下,路由器充当防火墙,阻止入站流量到达您的服务器。
基本的解决方法有:
1)打开 UPnP
这样可以使您的应用程序指示路由器如何将入站流量转发回您的服务器。
2)设置端口转发
通过手动配置路由器来实现上述功能。
3)使您的工作服务器成为客户端
路由器允许出站连接启动连接。它记住返回地址,重写外部可见 IP,并提供一个未使用的端口,以便外部流量可以回话(NAT)。这允许出站请求与外部建立通信并绕过防火墙。如果您的家庭 IP 是固定的,则可以在工作中设置客户端,尝试按计划拨打家庭电话(直到您启动服务器并建立连接)。
4)使用 P2P(调解服务器)
我不确定你从哪里开始,但原则是这样的。它通常在单个UDP端口上工作。用于建立连接的服务器不在NAT后面。客户端将其IP以UDP数据包发送到服务器,并且路由器使用路由器返回地址重新编写UDP标头。服务器获取此数据并将其发送给其他对等方。现在,每个人都知道彼此的返回地址,他们可以直接向彼此发送TCP流量,而服务器则退出。
这里有一些关于NAT基础知识的非常好的文章here,用简单的术语解释。还有一篇好文章here,解释了P2P如何利用NAT绕过防火墙。
希望这能给你一些想法。

感谢您的帮助。如果您能告诉我为什么我仍然无法进行TCP打洞,那将很好。请查看我的问题更新。如果您能使其正常工作,我将非常感激。 - Tono Nam
我相信你正在尝试上述的第四种方法。这需要中间服务器不在NAT后面,否则返回地址会因为被路由器重写而丢失,在到达你的服务器之前。理想情况下,你需要将路由器的出站地址/端口传递给每台机器,以便将它们连接在一起。返回地址将是一些你无法预测的随机端口,只有两个路由器知道。 - TheCodeKing

4
TCP打洞经常不起作用。你最好采用UDP打洞。如果需要类似TCP的行为,可以使用RDP或类似协议,该协议提供TCP行为但可以使用UDP作为传输。
另一种方法是通过服务器中继所有流量。每个主机都可以连接到服务器,服务器可以将一个连接的流量复制到另一个连接。
最好的解决方案是如果你能从路由器获得一些支持,例如端口转发或UPnP。

我想避免使用中继,因为那会在服务器上消耗大量的CPU使用率。但是我会进一步研究UPnP。非常感谢! - Tono Nam
我怀疑。一个 '486 可以中继 100Mbps。 - David Schwartz
我已经成功实现了UDP打洞。我正在传输文件,所以我想使用TCP协议。无论如何,正如你所说的那样,这似乎很难实现。我认为在使用UDP协议时应该可以确保不会丢失数据,对吧? - Tono Nam
是的,你要确保不会像TCP一样丢失数据——你需要实现重传、确认、传输节奏等功能。 - David Schwartz
假设字节数组Data1的长度为2048。当我调用client.Send(Data1)时,会发送多少个数据包?为了尝试您所说的内容,我可以发送每个数据包的最大字节数是多少? - Tono Nam
显示剩余6条评论

2
这里有一篇关于UDP和TCP打洞技术的优秀文章。

http://www.brynosaurus.com/pub/net/p2pnat/

“然而,使用这种打洞技术需要一个知名的会合服务器,我不认为你想要设置它。”
“顺便说一下,你需要仔细检查公司关于在办公室拥有自己的服务器的政策。出于安全考虑,我不认为公司允许员工在内部设置自己的服务器。”

你的链接非常有帮助。我相信我按照你链接中提到的做了。但我不明白为什么它不起作用。请看一下我的更新... - Tono Nam

2
你可以编写自己的代理程序:
服务器端: 监听1300端口以接收来自A的连接请求,同时监听1301端口以接收来自B的连接请求。维护一个连接列表,在至少有A和B两个连接时创建代理对象。此时,向来自B的连接发送一个信号,通知其已经建立了连接。该信号可以是一个字节或一个用于连接的端口和地址。之后,当从A端接收到数据时,将其发送到B端;当从B端接收到数据时,将其发送到A端。
计算机B端: 程序维护与服务器上1301端口的连接。如果连接中断,则重新建立连接。当收到信号时(信号可能包含地址和端口信息,也可能只是一个表示“我已经连接好了”的字节),创建到所需端口的连接,并将这两个连接存储在代理对象中。当从一个连接方接收到数据时,发送给另一个连接方。由于正在使用该连接,因此需要建立一个新的连接到服务器上的1301端口以处理更多的连接请求。
当然,你还需要处理连接中断的情况。通过在B和服务器之间保持始终打开的待处理连接发送保持活动信号可以帮助解决该问题。
这是我很久以前写的一个示例类,用来进行代理。我没有时间清理它,但如果你看到TcpProxy,那是一个接受连接的父类,客户端是被接受的连接,RemoteEndPoint是要连接的终点。它还将数据写入文件并做一些其他你可以忽略的事情。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接