【问题标题】:python server-client nat traversalpython server-client nat 遍历
【发布时间】:2015-09-21 11:35:07
【问题描述】:

我已经阅读了一些有关 UDP NAT 遍历的内容,并且我有理由相信自己了解基础知识,但我仍在为实现而苦苦挣扎。

我的项目有一个全局可访问的服务器,以及 nat 后面的客户端。它是一个游戏,基本的 join_game 请求从客户端发送到服务器,然后服务器在每个间隔发送更新。我一直在家里测试,忘记了我的路由器上的 DMZ 已打开,所以它工作正常。我把这个发给一些朋友测试,他们无法从服务器接收更新。

这是当前的方法,所有数据包都是 UDP:

  • 客户端打开一个套接字,并向服务器发送加入请求。
  • 服务器获取请求和消息的回复地址,并回复客户端:是的,您可以加入,顺便说一下,我将向您的回复 IP/端口发送更新,看起来像这样。
  • 客户端捕获回复然后关闭套接字,并启动一个 UDP 线程侦听器类来侦听服务器告诉我们的回复端口。
  • 客户端然后捕获被淹没的服务器更新,并根据需要处理它们。客户端不时打开一个新套接字并向服务器发送一个带有更新的 UDP 数据包(按下了哪些键等)。

我的理解是服务器接收到的回复地址应该有正确的端口来遍历客户端的nat。并且经常向那里发送数据包将使 nat 遍历规则保持有效。

这不会发生。客户端发送加入请求,并在该套接字上接收服务器的响应。但是当我关闭套接字然后在回复端口上启动一个线程化的 UDP 侦听器时,它什么也抓不到。就好像遍历规则只对单个响应包有效。

如果需要,我可以包含代码,但老实说,它有几个层次的类和对象,它完成了我上面描述的操作。代码在我打开 DMZ 时有效,但在关闭时无效。

我将包含一些感兴趣的 sn-ps。

这是加入请求的服务器处理程序。 client_address 是从线程处理程序向下传递的,它是 SocketServer.BaseRequestHandler 属性 self.client_address。不用解析,直接传下来。

def handle_player_join(self, message, reply_message, client_address):

        # Create player id
        player_id = create_id()

        # Add player to the connected nodes dict
        self.modules.connected_nodes[player_id] = client_address

        # Create player ship entity
        self.modules.players[player_id] = self.modules.factory.player_ship( position     = (320, 220),
                                                                            bearing      = 0,
                                                                          )

        # Set reply to ACK, and include the player id and listen port
        reply_message.body                  = Message.ACK
        reply_message.data['PLAYER_ID']     = player_id
        reply_message.data['LISTEN_PORT']   = client_address[1]

        print "Player Joined :"+str(client_address)+", ID: "+str(player_id)

        # Return reply message
        return reply_message

一位朋友提到,也许当我发送加入请求时,得到响应时我不应该关闭套接字。保持该套接字处于活动状态,并使其成为侦听器。我不相信关闭套接字会对 nat 遍历产生任何影响,而且我不知道如何生成一个线程 udp 侦听器,该侦听器采用预先存在的套接字而不重写整个该死的东西(我宁愿不)。

需要任何想法或信息吗?

干杯

【问题讨论】:

  • 通常 NAT 由路由器/防火墙完成。客户端与 NAT 无关,实际上客户端只知道将数据包发送到哪里。不知道你有什么网络设置?也许您朋友那边的路由器/防火墙没有正确配置为执行 NAT?它是什么类型的 NAT?你是怎么在家测试的?局域网中的服务器和客户端之间是否有路由器? Im not convinced closing the socket will have any effect on the nat traversal 万一出现PAT 效果!
  • 我的朋友有一个标准的路由器/调制解调器,您可以通过任何互联网套餐免费获得。正常的互联网进入调制解调器,家里的基本局域网。真的和我的设置一样,除了我为我的机器打开了 DMZ。关闭 DMZ 使其停止工作。 python 被打包成一个 pyinstaller 可执行文件,当你运行它时,Win7 会请求将它连接到网络的权限(他们接受了)。我在没有打开 DMZ 的机器上也纯粹从脚本运行它,但它不起作用。我们的网络设置没有什么异常:(
  • 你是什么意思,如果 PAT 有影响?
  • 很难调查您的问题,因为它更多的是网络问题而不是编程问题。打开 DMZ 可能会对调制解调器/防火墙的过滤规则进行一些更改,并且您的代码在允许网络连接时工作。 PAT 通常在IP + Port number 的基础上进行。 Socket = IP+Port no.,所以现在你看到了端口号。影响 PAT。当端口关闭时,相应的 PAT 条目将从防火墙中删除。试试 superuser.com 以获得更好的答案!
  • hmmmm 我不熟悉 PAT。这是否完全在客户端 PC 上运行?所以当你关闭套接字时你会丢失映射?

标签: python nat


【解决方案1】:

您可以执行以下两种操作中的任何一种来使您的代码正常工作。他们是,

不要关闭您将数据包发送到服务器的套接字。当您创建一个套接字时,它会绑定到一个私有 IP:端口。当您向服务器发送数据包时,IP:Port 将被转换为您的 NAT 一个公共 IP:Port。现在,当您关闭此套接字时,来自服务器的数据首先到达您的 NAT 公共 IP:端口,然后转发到您的私有 IP:端口。但是由于您的套接字已关闭,因此没有人会收到该数据。现在服务器无法知道您已经使用新的私有 IP:Port 创建了一个新套接字,因为在创建这个新套接字后您从未向您的服务器发送数据包。所以不要关闭旧的套接字。试着在一个线程中听这个旧的。或者你可以从新的套接字向服务器发送一个数据包,让它知道你新翻译的公共 IP:端口。这样该服务器就可以将其数据发送到这个新的公共 IP:Port,然后将其转发到您的新私有 IP:Port。

关闭套接字但重复使用相同的端口。当您关闭旧套接字并创建新套接字时,将其绑定到绑定旧套接字的端口。这不会更改 NAT 的公共 IP:端口,并且来自您服务器的数据不会被中断。

【讨论】:

    猜你喜欢
    • 2017-10-17
    • 2021-05-28
    • 2011-06-03
    • 1970-01-01
    • 2015-10-13
    • 2016-12-11
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    相关资源
    最近更新 更多