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))
upnp_completed.emit(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")
upnp_completed.emit(OK)
func _ready():
thread = Thread.new()
thread.start(_upnp_setup.bind(SERVER_PORT))
func _exit_tree():
# 在此等待线程完成运行,以便在该线程运行期间处理应用退出事宜。
thread.wait_to_finish()
术语: 术语:在 UPnP 网络的语境中,“网关”(或“互联网网关设备”,简称 IGD)指的是能够让本地网络中的计算机接入互联网(“广域网”,WAN)的网络设备。这些网关通常也被称为“路由器”。
常见问题:
如上所述,这些调用是阻塞式的,不应在主线程中运行,尤其是因为它们可能会一次性阻塞数秒之久。请使用线程!
网络是物理性的且十分复杂混乱的。数据包在传输过程中可能会丢失或被过滤,地址、空闲端口和分配的映射可能会发生变化,而且设备随时都可能离开或加入网络。请务必留意这一点,在检查和处理错误时要勤勉认真,并且如果可能的话,要妥善处理这些情况:添加清晰的错误界面、设置超时机制以及进行重试处理。
端口映射可能会随时发生变化(甚至被删除),网关的远程/外部 IP 地址也可能随之改变。您应当考虑重新查询外部 IP 地址,并尝试定期(例如每 5 分钟一次以及在网络出现故障时)更新/刷新端口映射。
并非所有设备都支持 UPnP 功能,而且有些用户会禁用 UPnP 支持。您需要对此进行处理(例如,记录相关事项并要求用户手动设置端口转发,或者添加其他网络穿越方法,比如中继/镜像服务器、NAT 穿越技术(如 STUN/TURN)等)。
考虑一下在出现冲突时会发生什么情况。也许同一网络中的多个用户都想在同一时间玩你的应用,又或者另一个应用程序使用了相同的端口。将端口设置为可配置的,并且最好能自动选择一个端口(在失败时尝试使用不同的端口)。
进一步阅读建议: 如果您想更深入地了解 UPnP(以及具体的互联网网关设备(IGD)和端口控制协议(PCP)), Wikipedia 是一个很好的起点,其规范可在 Open Connectivity Foundation 上找到,而 i3D 的实现则是基于 MiniUPnP client 。
属性¶
方法¶
void |
add_device(device: UPNPDevice) |
add_port_mapping(port: int, port_internal: int = 0, desc: String = "", proto: String = "UDP", duration: int = 0) const |
|
void |
|
delete_port_mapping(port: int, proto: String = "UDP") const |
|
discover(timeout: int = 2000, ttl: int = 2, device_filter: String = "InternetGatewayDevice") |
|
get_device(index: int) const |
|
get_device_count() const |
|
get_gateway() const |
|
query_external_address() const |
|
void |
remove_device(index: int) |
void |
set_device(index: int, device: UPNPDevice) |
枚举¶
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
未知错误。
属性说明¶
如果为 true,则 IPv6 用于 UPNPDevice 发现。
如果为 0,系统会自动选择用于发现的本地端口。如果为 1,将从源端口 1900 进行发现(与目的端口相同)。否则,将使用该值作为端口。
String discover_multicast_if = "" 🔗
用于发现的多播接口。如果为空,则使用默认的多播接口。
方法说明¶
void add_device(device: UPNPDevice) 🔗
将给定的 UPNPDevice 添加到已发现设备的列表中。
int add_port_mapping(port: int, port_internal: int = 0, desc: String = "", proto: String = "UDP", duration: int = 0) const 🔗
会添加一条映射规则,用于将默认网关(见 get_gateway() )上的外部 port 范围在 1 到 65535 之间,但建议使用 1024 及以上的端口)转发到本地机器上的 port_internal (具体端口取决于所选协议 proto ("TCP" 或者 "UDP", 默认是 "UDP")如果该网关设备上已经存在针对给定端口和协议组合的端口映射,则此方法会尝试覆盖它。如果不想这样做,您可以手动使用 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(port: int, proto: String = "UDP") const 🔗
如果默认网关上存在对给定端口和协议组合的端口映射,则将其删除(见 get_gateway())。port 必须是 1 和 65535 之间的有效端口,proto 可以是 "TCP" 或 "UDP"。拒绝的原因可能是映射指向其他地址、端口为公认端口(1024 以下)、映射不是由 UPnP 添加的。可能的返回值见 UPNPResult。
int discover(timeout: int = 2000, ttl: int = 2, device_filter: String = "InternetGatewayDevice") 🔗
发现本地的 UPNPDevice清除之前发现的设备列表。
对于 IGD(互联网网关设备)类型的设备,默认情况下会启用过滤功能,因为这些设备负责端口转发。 timeout 指的是等待响应的时间(以毫秒为单位)。 ttl 是生存时间;只有在您完全了解其含义的情况下才应设置此值。
请参阅 UPNPResult 以了解可能的返回值。
UPNPDevice get_device(index: int) 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(index: int) 🔗
将 index 处的设备从已发现的设备列表中移除。
void set_device(index: int, device: UPNPDevice) 🔗
将 index 处的设备从已发现的设备列表中设置为 device。