Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
UPNP¶
继承: RefCounted < Object
通用即插即用(UPnP)功能,用于网络设备的发现、查询及端口映射。
描述¶
这个类可用于在本地网络中发现兼容的 UPNPDevice 并在这些设备上执行命令,如管理端口映射(用于端口转发/NAT 穿透)和查询本地及远程网络 IP 地址。请注意,这个类的方法都是同步的,会阻塞调用线程。
要转发指定端口(此处为 7777
,请注意 discover 和 add_port_mapping 都可能返回错误,应进行检查):
var upnp = UPNP.new()
upnp.discover()
upnp.add_port_mapping(7777)
要关闭指定端口(例如结束使用后):
upnp.delete_port_mapping(port)
注意:UPnP 发现会阻塞当前线程。要在不阻塞主线程的前提下执行发现,请像这样使用 Thread:
# UPnP 端口映射建立完成时发出(无论成败)。
signal upnp_completed(error)
# 请将其替换为你自己的服务器端口号,在 1024 和 65535 之间。
const SERVER_PORT = 3928
var thread = null
func _upnp_setup(server_port):
# UPNP 查询比较耗时。
var upnp = UPNP.new()
var err = upnp.discover()
if err != OK:
push_error(str(err))
emit_signal("upnp_completed", err)
return
if upnp.get_gateway() and upnp.get_gateway().is_valid_gateway():
upnp.add_port_mapping(server_port, server_port, ProjectSettings.get_setting("application/config/name"), "UDP")
upnp.add_port_mapping(server_port, server_port, ProjectSettings.get_setting("application/config/name"), "TCP")
emit_signal("upnp_completed", OK)
func _ready():
thread = Thread.new()
thread.start(_upnp_setup.bind(SERVER_PORT))
func _exit_tree():
# 游戏退出但线程还在运行时,在此处等待线程完成。
thread.wait_to_finish()
术语:UPnP 网络中,“网关”(gateway,或称“互联网网关设备”,internet gateway device,简称 IGD)指的是在局域网中让计算机能够访问互联网(“广域网”,wide area network,WAN)的网络设备。这些网关经常也叫做“路由器”。
陷阱:
前文解释过,这些调用都是阻塞的,不应该在主线程上执行,一次就能阻塞上很多秒。用用线程吧!
网络是实打实的混乱。数据包可能会在传输过程中丢失或者被过滤掉,地址、空闲端口、端口映射有可能发生变化,设备可以随时离开或者加入网络。请考虑周全,老老实实地检查错误并进行处理,处理错误时请尽量友好:添加简洁的报错 UI、超时处理、重试机制。
端口映射是随时会变的(也可以被删除),网关的远程/外部 IP 也可能发生改变。你应该考虑定期重新查询外部 IP、尝试更新/刷新端口映射(例如每隔 5 分钟或者在发生网络错误时执行)。
并不是所有的设备都支持 UPnP,有些用户还会禁用 UPnP 支持。你需要处理这种情况(例如编写文档,要求用户手动进行端口映射,或者加入接力/镜像服务器、NAT 打洞、STUN/TURN 等 NAT 穿透的备用方案)。
请考虑映射冲突时该怎么办。可能在同一个网络上同时有多个用户想要来玩你的游戏,或者有其他应用程序用了一样的端口。请把端口号做成可配置的,最好能够自动选择(失败时重试其他端口)。
拓展阅读:如果你想了解更多关于 UPnP(尤其是 Internet Gateway Device(IGD)和 Port Control Protocol(PCP)),可以首先查看维基百科,技术规范可以在 Open Connectivity 基金会找到,Godot 的实现基于的是 MiniUPnP 客户端。
属性¶
|
||
|
||
|
方法¶
void |
add_device ( UPNPDevice device ) |
add_port_mapping ( int port, int port_internal=0, String desc="", String proto="UDP", int duration=0 ) const |
|
void |
clear_devices ( ) |
delete_port_mapping ( int port, String proto="UDP" ) const |
|
discover ( int timeout=2000, int ttl=2, String device_filter="InternetGatewayDevice" ) |
|
get_device ( int index ) const |
|
get_device_count ( ) const |
|
get_gateway ( ) const |
|
query_external_address ( ) const |
|
void |
remove_device ( int index ) |
void |
set_device ( int index, UPNPDevice device ) |
枚举¶
enum UPNPResult:
UPNPResult UPNP_RESULT_SUCCESS = 0
UPNP 命令或发现成功。
UPNPResult UPNP_RESULT_NOT_AUTHORIZED = 1
未授权在 UPNPDevice 上使用该命令。当用户在其路由器上禁用 UPNP 时,可能会被返回。
UPNPResult UPNP_RESULT_PORT_MAPPING_NOT_FOUND = 2
在给定的 UPNPDevice 上没有找到给定端口、协议组合的端口映射。
UPNPResult UPNP_RESULT_INCONSISTENT_PARAMETERS = 3
参数不一致。
UPNPResult UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY = 4
数组中没有此条目。如果在 UPNPDevice 上没有找到给定的端口、协议组合,可能会被返回。
UPNPResult UPNP_RESULT_ACTION_FAILED = 5
操作失败。
UPNPResult UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED = 6
UPNPDevice 不允许源 IP 地址的通配符值。
UPNPResult UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED = 7
UPNPDevice 不允许外部端口的通配符值。
UPNPResult UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED = 8
UPNPDevice 不允许内部端口的通配符值。
UPNPResult UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD = 9
远程主机值必须是通配符。
UPNPResult UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD = 10
外部端口值必须是通配符。
UPNPResult UPNP_RESULT_NO_PORT_MAPS_AVAILABLE = 11
没有可用的端口映射。如果端口映射功能不可用,也可能被返回。
UPNPResult UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM = 12
与其他机制冲突。如果一个端口映射与现有的冲突,可能会被返回,而不是UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING。
UPNPResult UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING = 13
与现有的端口映射相冲突。
UPNPResult UPNP_RESULT_SAME_PORT_VALUES_REQUIRED = 14
外部和内部端口值必须相同。
UPNPResult UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED = 15
只支持永久租用。在添加端口映射时,不要使用 duration
参数。
UPNPResult UPNP_RESULT_INVALID_GATEWAY = 16
无效网关。
UPNPResult UPNP_RESULT_INVALID_PORT = 17
无效端口。
UPNPResult UPNP_RESULT_INVALID_PROTOCOL = 18
无效协议。
UPNPResult UPNP_RESULT_INVALID_DURATION = 19
无效持续时间。
UPNPResult UPNP_RESULT_INVALID_ARGS = 20
无效参数。
UPNPResult UPNP_RESULT_INVALID_RESPONSE = 21
无效响应。
UPNPResult UPNP_RESULT_INVALID_PARAM = 22
无效参数。
UPNPResult UPNP_RESULT_HTTP_ERROR = 23
HTTP 错误。
UPNPResult UPNP_RESULT_SOCKET_ERROR = 24
套接字错误。
UPNPResult UPNP_RESULT_MEM_ALLOC_ERROR = 25
分配内存时出错。
UPNPResult UPNP_RESULT_NO_GATEWAY = 26
没有可用的网关。你可能需要先调用 discover ,否则发现没有检测到任何有效的 IGD(InternetGatewayDevices)。
UPNPResult UPNP_RESULT_NO_DEVICES = 27
没有可用的设备。你可能需要先调用 discover,或者发现没有检测到任何有效的 UPNPDevice。
UPNPResult UPNP_RESULT_UNKNOWN_ERROR = 28
未知错误。
属性说明¶
bool discover_ipv6 = false
如果为 true
,则 IPv6 用于 UPNPDevice 发现。
int discover_local_port = 0
如果为 0
,系统会自动选择用于发现的本地端口。如果为 1
,将从源端口 1900 进行发现(与目的端口相同)。否则,将使用该值作为端口。
String discover_multicast_if = ""
用于发现的多播接口。如果为空,则使用默认的多播接口。
方法说明¶
void add_device ( UPNPDevice device )
将给定的 UPNPDevice 添加到已发现设备的列表中。
int add_port_mapping ( int port, int port_internal=0, String desc="", String proto="UDP", int duration=0 ) const
添加映射,针对给定的协议 proto
("TCP"
或 "UDP"
,默认为 UDP),将默认网关(见 get_gateway)上的外部端口 port
(在 1 到 65535 之间,不过推荐使用 1024 以上的端口)映射到本机上的内部端口 port_internal
。如果该网关上已经存在给定的端口与协议的组合,这个方法会尝试进行覆盖。如果不希望如此,你可以使用 get_gateway 手动获取网关,获取到后调用其 add_port_mapping 方法。请注意,使用 UPnP 转发公认端口(1024 以下)在有些设备上可能会失败。
如果端口的映射已存在,有些网关设备可能会对其进行更新,有些则会因为冲突而拒绝这个命令,尤其当现有端口映射不是由 UPnP 创建的,或者指向的是别的网络地址(或设备)的时候。
如果 port_internal
为 0
(默认),表示内外部端口相同(使用 port
的值)。
描述(desc
)会显示在一些路由器的管理界面上,可以用来识别添加映射的程序。
映射的租赁时长 duration
可以通过指定秒数来限定。默认的 0
表示没有时长,即永久租赁,有些设备只支持这种永久租赁。请注意,无论是否永久都只是一种请求,网关仍然可以随时移除映射(通常发生在重启网关后外部 IP 地址发生变化时,也有些型号会在映射不再活动,即若干分钟无流量时移除)。如果非 0
(永久),技术规格所允许的范围是 120
(2 分钟)到 86400
秒(24 小时)。
可能的返回值见 UPNPResult。
void clear_devices ( )
清除已发现设备的列表。
int delete_port_mapping ( int port, String proto="UDP" ) const
如果默认网关上存在对给定端口和协议组合的端口映射,则将其删除(见 get_gateway)。port
必须是 1 和 65535 之间的有效端口,proto
可以是 "TCP"
或 "UDP"
。拒绝的原因可能是映射指向其他地址、端口为公认端口(1024 以下)、映射不是由 UPnP 添加的。可能的返回值见 UPNPResult。
int discover ( int timeout=2000, int ttl=2, String device_filter="InternetGatewayDevice" )
发现本地的 UPNPDevice。清除先前发现的设备的列表。
默认情况下会过滤 IGD(InternetGatewayDevice)类型的设备,因为这些设备管理端口转发。timeout
是等待响应的时间,单位为毫秒。ttl
是生存时间;请在你知道自己在做什么的时候才碰这个参数。
可能的返回值见 UPNPResult。
UPNPDevice get_device ( int index ) const
返回给定 index
处的 UPNPDevice。
int get_device_count ( ) const
返回已发现的 UPNPDevice 的数量。
UPNPDevice get_gateway ( ) const
返回默认网关。这是第一个发现的UPNPDevice,也是一个有效的IGD(InternetGatewayDevice)。
String query_external_address ( ) const
返回默认网关的外部 IP 地址字符串(见 get_gateway)。错误时返回空字符串。
void remove_device ( int index )
将 index
处的设备从已发现的设备列表中移除。
void set_device ( int index, UPNPDevice device )
将 index
处的设备从已发现的设备列表中设置为 device
。