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(UPNP 裝置),並對它們執行命令,比如管理連接埠映射(用於連接埠轉發 / NAT 穿透)以及查詢本機和遠端網路的 IP 位址。需要注意的是,這個類中的方法都是同步的,並且會阻塞(卡住)呼叫它的執行緒。
要轉發一個特定的連接埠(這裡是 7777,注意 discover() 和 add_port_mapping() 都有可能回傳錯誤,程式碼中應該對這些錯誤進行檢查):
var upnp = UPNP.new()
upnp.discover()
upnp.add_port_mapping(7777)
To close a specific port (e.g. after you have finished using it):
upnp.delete_port_mapping(port)
Note: UPnP discovery blocks the current thread. To perform discovery without blocking the main thread, use Threads like this:
# 當 UPnP 連接埠映射設定完成時發出該訊號(無論成功還是失敗)。
signal upnp_completed(error)
# 請將其替換為你自己的伺服器連接埠號(範圍在 1024 到 65535 之間)。
const SERVER_PORT = 3928
var thread = null
func _upnp_setup(server_port):
# UPNP queries take some time.
var upnp = UPNP.new()
var err = upnp.discover()
if err != UPNP.UPNP_RESULT_SUCCESS:
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(UPNP.UPNP_RESULT_SUCCESS)
func _ready():
thread = Thread.new()
thread.start(_upnp_setup.bind(SERVER_PORT))
func _exit_tree():
# 在此處等待執行緒執行完畢,以便線上程仍在執行時妥善處理遊戲退出。
thread.wait_to_finish()
術語說明: 在 UPnP 網路的語境中,“網關”(或稱“互連網網關裝置”,簡稱 IGD)指的是那些允許本機網路中的電腦存取互連網(即“廣域網”,WAN)的網路裝置。這些網關通常也被稱為“路由器”。
常見陷阱:
正如上文所述,這些呼叫都是阻塞式的,因此不應該在主執行緒上執行,尤其是考慮到它們可能會一次性卡住好幾秒鐘。請務必使用多執行緒!
網路環境是物理層面的,且充滿了各種不確定性。封包在傳輸中可能會丟失或被過濾,IP 位址、空閒連接埠以及已分配的映射關係隨時可能發生變化,裝置也可能隨時加入或離開網路。請務必考慮到這些因素,在檢查和處理錯誤時要格外仔細;如果可能的話,請優雅地應對這些問題:提供清晰的錯誤提示介面(UI),並設定超時與重試機制。
連接埠映射隨時可能發生變化(甚至被移除),同樣地,網關的遠端/外部 IP 位址也可能發生改變。你應該考慮定期重新查詢外部 IP,並嘗試更新/刷新連接埠映射(例如,每隔 5 分鐘以及在發生網路故障時進行更新)。
並非所有的裝置都支援 UPnP,而且有些使用者會主動停用 UPnP 功能。你需要對此做好應對準備(例如,在文件中說明並讓使用者手動轉發連接埠,或者加入其他替代的 NAT 穿透方法,比如使用中繼/鏡像伺服器、NAT 打洞、STUN/TURN 等)。
請考慮發生映射衝突時會發生甚麼。也許同一個網路下的多個使用者想要同時玩你的遊戲,或者也許有其他應用程式佔用了同一個連接埠。請將連接埠設定為可設定的,並且最好能自動選擇一個連接埠(在失敗時嘗試使用不同的連接埠進行重試)。
拓展閱讀: 如果你想瞭解更多關於 UPnP(特別是互連網網關裝置 IGD 和連接埠控制協議 PCP)的資訊,維基百科 是一個不錯的入門站點;相關規範可以在 開放連接基金會(Open Connectivity Foundation) 找到;而 Godot 的實作則是基於 MiniUPnP 用戶端。
屬性
|
||
|
||
|
方法
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 🔗
新增對應,針對給定的協議 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(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(InternetGatewayDevice)型別的裝置,因為這些裝置管理埠轉發。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。