如何在不端口转发的情况下通过UDP接收数据

0 投票
2 回答
638 浏览
提问于 2025-04-18 17:59

首先,我对套接字编程完全是个新手,所以如果我做错了什么,请告诉我。

我一直在尝试为一个简单的第一人称射击游戏编写一个UDP服务器。

在本地网络上,一切都运行得很好,但如果我尝试在互联网上使用我的代码,就会遇到一些问题。我的服务器可以毫无问题地接收来自客户端的数据,并且可以向客户端发送数据,只要客户端的端口是开放的。如果端口没有开放,客户端就收不到任何数据。(服务器上的所有端口都是开放的。)我现在的问题是,有没有办法在互联网上接收数据,而不需要打开或转发端口?

(client()函数在一个循环中不断重复)

我接收数据的代码如下:

import socket

server_IP = 'ip of server'
Client_port = 3560
Server_port = 5000
Client_socket = None
First_run = True
first = True

def client():
    global server_IP
    global Client_port
    global Server_port
    global Client_socket
    global first
    if first == True:
        try:
            local = ('', Client_port)
            host = (server_IP, Server_port)
            Client_socket  = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            Client_socket.bind(local)
            Client_socket.setblocking(False)
            first = False
        except:
            print("Failed")
    try:
        data, connection = Client_socket.recvfrom(512)
    except socket.error:
        return
    if data:
        #irrelevant code handling the data...

而服务器发送数据的代码是这样的:

import socket
server_socket = None
server_port = 5000
Data_Port = 3560
print("importing the socket...")
try:
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
except socket.error as msg:
    server_socket = None
try:
    server_socket.bind(("", server_port))
except socket.error as msg:
    server_socket.close()
    server_socket = None
if server_socket is None:
    print('could not open socket')
    ret = False
server_socket.sendto(Data_to_Send, ('client ip here',Data_Port))

当然,这只是代码的一部分,但我想这些是相关的部分。如果没有办法在我发送/接收数据的方式下接收数据而不需要打开或转发端口,请建议一种可以做到的方法,如果有的话。

2 个回答

0

这个问题很难找到清晰的答案,但我终于搞定了,感谢Martin Konecny关于“打孔”的解释(比如http://en.wikipedia.org/wiki/UDP_hole_punching),还有我自己反复尝试的经验。

我之前犯的错误主要是因为我在客户端关闭了用来发送数据的那个socket,然后又试图在一个新的socket上监听服务器的回复。这是行不通的,或者说我没能让它工作。不过,如果我不关闭那个socket,而是在同一个socket上监听回复,事情就顺利多了。结果发现,我甚至不需要像问题中那样去绑定socket,这样也能正常工作。

1

来看看UDP打洞的概念:

http://en.wikipedia.org/wiki/UDP_hole_punching

简单来说,客户端首先要连接到你的服务器,这样就会在NAT路由表中创建一个记录。然后,服务器会查看这个请求,看看NAT路由器使用了哪个端口来发送请求。你应该能够向这个端口发送一个UDP数据包,这样数据包就会被转发到私有网络中的客户端。

总结一下,不要把你的端口死死写成3560。你需要根据UDP数据包的来源来改变这个端口。

你可能还想考虑使用TCP协议,它是基于连接的协议(而UDP是无连接的)——当客户端和服务器建立连接后,数据在这个连接上来回发送就变得非常简单。

撰写回答