python server-client nat穿越
python server-client nat traversal
我已经阅读了一些有关 UDP NAT 遍历的资料,我有理由相信我了解基础知识,但我仍在为实现而苦苦挣扎。
我的项目有一个全球可访问的服务器,以及 nat 后面的客户端。这是一个游戏,基本的 join_game 请求从客户端发送到服务器,然后服务器每隔一段时间发送更新。我一直在家里测试,忘记了我在路由器上打开了 DMZ,所以它工作正常。我发给一些朋友测试,他们收不到服务器的更新。
这是当前的方法,所有数据包都是 UDP:
- 客户端打开套接字,向服务器发送加入请求。
- 服务器获取消息的请求和回复地址,并回复客户端:是的,你可以加入,顺便我会发送更新到你的回复ip/port 看起来像这样。
- 客户端捕获回复然后关闭套接字,并启动 UDP 线程侦听器 class 以侦听服务器告诉我们的回复端口。
- 客户端然后捕获被淹没的服务器更新,并根据需要处理它们。客户端时不时地打开一个新套接字并向服务器发送一个带有更新的 UDP 数据包(按下了什么键等)。
我的理解是服务器接收到的回复地址应该有正确的端口来遍历客户端的nat。经常向那里发送数据包将使 nat 遍历规则保持有效。
这不会发生。客户端发送加入请求,并在该套接字上接收服务器的响应。但是当我关闭套接字然后在回复端口上启动一个线程化的 UDP 侦听器时,它什么也抓不到。几乎就像遍历规则只对单个响应数据包有效。
如果需要,我可以包含代码,但老实说,它有多个层 classes 和对象,它执行我上面描述的操作。该代码在我打开 DMZ 时有效,但在关闭时无效。
我将包括一些感兴趣的片段。
这是加入请求的服务器处理程序。 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 侦听器,它采用预先存在的套接字而不重写整个该死的东西(我宁愿不重写).
需要任何想法或信息吗?
干杯
您可以执行以下两项操作中的任何一项来使您的代码正常工作。他们是,
不要关闭您向服务器发送数据包的套接字。当您创建套接字时,它会绑定到私有 IP:Port。当您向服务器发送一个数据包时,IP:Port 将被转换为您的 NAT 一个 public IP:Port。现在,当您关闭此套接字时,来自服务器的数据首先到达您的 NAT public IP:Port,然后转发到您的私有 IP:Port。但是由于您的套接字已关闭,因此没有人会收到该数据。现在服务器没有办法知道你已经创建了一个新的套接字 new private IP:Port 因为你在创建这个新套接字后从未向你的服务器发送过数据包。所以不要关闭旧的插座。试着在一个线程中听这个旧的。或者您可以从新套接字向服务器发送一个数据包,让它知道您的新翻译public IP:Port。这样该服务器就可以将其数据发送到这个新的 public IP:Port,后者又会转发到您的新私人 IP:Port.
关闭套接字但重新使用同一个端口。当您关闭旧套接字并创建新套接字时,将其绑定到旧套接字绑定的端口。这不会更改 NAT public IP:Port,并且来自您服务器的数据不会中断。
我已经阅读了一些有关 UDP NAT 遍历的资料,我有理由相信我了解基础知识,但我仍在为实现而苦苦挣扎。
我的项目有一个全球可访问的服务器,以及 nat 后面的客户端。这是一个游戏,基本的 join_game 请求从客户端发送到服务器,然后服务器每隔一段时间发送更新。我一直在家里测试,忘记了我在路由器上打开了 DMZ,所以它工作正常。我发给一些朋友测试,他们收不到服务器的更新。
这是当前的方法,所有数据包都是 UDP:
- 客户端打开套接字,向服务器发送加入请求。
- 服务器获取消息的请求和回复地址,并回复客户端:是的,你可以加入,顺便我会发送更新到你的回复ip/port 看起来像这样。
- 客户端捕获回复然后关闭套接字,并启动 UDP 线程侦听器 class 以侦听服务器告诉我们的回复端口。
- 客户端然后捕获被淹没的服务器更新,并根据需要处理它们。客户端时不时地打开一个新套接字并向服务器发送一个带有更新的 UDP 数据包(按下了什么键等)。
我的理解是服务器接收到的回复地址应该有正确的端口来遍历客户端的nat。经常向那里发送数据包将使 nat 遍历规则保持有效。
这不会发生。客户端发送加入请求,并在该套接字上接收服务器的响应。但是当我关闭套接字然后在回复端口上启动一个线程化的 UDP 侦听器时,它什么也抓不到。几乎就像遍历规则只对单个响应数据包有效。
如果需要,我可以包含代码,但老实说,它有多个层 classes 和对象,它执行我上面描述的操作。该代码在我打开 DMZ 时有效,但在关闭时无效。
我将包括一些感兴趣的片段。
这是加入请求的服务器处理程序。 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 侦听器,它采用预先存在的套接字而不重写整个该死的东西(我宁愿不重写).
需要任何想法或信息吗?
干杯
您可以执行以下两项操作中的任何一项来使您的代码正常工作。他们是,
不要关闭您向服务器发送数据包的套接字。当您创建套接字时,它会绑定到私有 IP:Port。当您向服务器发送一个数据包时,IP:Port 将被转换为您的 NAT 一个 public IP:Port。现在,当您关闭此套接字时,来自服务器的数据首先到达您的 NAT public IP:Port,然后转发到您的私有 IP:Port。但是由于您的套接字已关闭,因此没有人会收到该数据。现在服务器没有办法知道你已经创建了一个新的套接字 new private IP:Port 因为你在创建这个新套接字后从未向你的服务器发送过数据包。所以不要关闭旧的插座。试着在一个线程中听这个旧的。或者您可以从新套接字向服务器发送一个数据包,让它知道您的新翻译public IP:Port。这样该服务器就可以将其数据发送到这个新的 public IP:Port,后者又会转发到您的新私人 IP:Port.
关闭套接字但重新使用同一个端口。当您关闭旧套接字并创建新套接字时,将其绑定到旧套接字绑定的端口。这不会更改 NAT public IP:Port,并且来自您服务器的数据不会中断。