Amazon GameLift Servers UDP ping 信標 - Amazon GameLift Servers

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

Amazon GameLift Servers UDP ping 信標

UDP ping 信標提供一種方法來測量玩家裝置和Amazon GameLift Servers託管位置之間的網路延遲。使用 ping 信標,您可以收集準確的延遲資料,以做出有關遊戲伺服器置放的明智決策,並根據延遲需求改善玩家配對。

UDP ping 信標的運作方式

Amazon GameLift Servers 在您可以部署遊戲伺服器的每個託管位置提供固定的 UDP 端點 (ping 信標)。由於大多數遊戲伺服器使用 UDP 進行通訊,因此與使用 ICMP ping 相比,使用 UDP ping 信標測量延遲可提供更準確的結果。網路裝置處理 ICMP 封包的方式通常與 UDP 封包不同,這可能會導致延遲測量,無法反映玩家將體驗到的實際效能。

使用 UDP ping 信標,您的遊戲用戶端可以傳送 UDP 訊息到這些端點並接收非同步回應,為您提供延遲測量,更能代表玩家裝置與潛在託管位置之間的實際遊戲流量條件。端點是永久性的,只要Amazon GameLift Servers支援在該位置託管遊戲,端點就會保持可用狀態。

UDP ping 信標的常見使用案例

您可以透過多種方式使用 UDP ping 信標來最佳化遊戲的聯網體驗。

選擇最佳託管位置

跨不同地理區域收集延遲資料,以識別託管玩家基礎遊戲伺服器的最佳主要和備份位置。

根據玩家延遲放置遊戲工作階段

在請求新的遊戲工作階段時包含玩家延遲資料,以協助挑選可提供最低延遲體驗的位置。

根據延遲最佳化配對

在請求配對時提供玩家延遲資料,以協助配對具有類似延遲設定檔的玩家,並將遊戲工作階段放置在配對玩家的最佳位置。

注意

建立配對請求時,您不應將延遲資訊提供給沒有機群的位置。如果您這麼做, Amazon GameLift Servers可能會嘗試將遊戲工作階段放置在沒有機群容量的位置,導致配對請求失敗。

取得信標端點

若要擷取Amazon GameLift Servers位置的 ping 信標網域和連接埠資訊,請使用 ListLocations API 操作。此 API 傳回的一組位置取決於 AWS 區域 您在呼叫它時指定的 (或者,如果您未指定預設區域)。當您從 呼叫 時:

  • 支援多位置機群的主區域:API 會傳回所有託管位置的資訊

  • 支援單一位置之機群的主區域:API 會傳回該位置的資訊

請注意,如果您使用只能是多位置機群中遠端位置的位置呼叫此 API,API 將傳回錯誤,因為該類型的位置沒有服務端點。

請參閱 中支援的位置表格支援 AWS 的位置,以識別支援單一和多位置機群的主區域。

範例

aws gamelift list-locations --region ap-northeast-2

這 AWS 區域 支援多位置機群,因此會傳回多個位置。以下是其中一個傳回值的範例:

[...] { "LocationName": "ap-northeast-1", "PingBeacon": { "UDPEndpoint": { "Domain": "gamelift-ping.ap-northeast-1.api.aws", "Port": 7770 } } }
重要

快取 ping 信標資訊,而不是在每次延遲測量ListLocations之前呼叫 。網域和連接埠資訊是靜態的,API 並非針對大量請求而設計。

實作延遲測量

使用 UDP ping 信標實作延遲測量時,請遵循下列最佳實務:

  1. 使用下列其中一種方法來存放 ping 信標資訊:

    • 硬式編碼遊戲用戶端中的端點。

    • 快取遊戲後端中的資訊。

    • 實作定期更新機制 (每日/每週) 以重新整理資訊。

  2. 傳送 UDP ping 訊息:

    • 只要訊息內文不是空的,且訊息的大小上限為 300 位元組,即可將您想要的任何內容放入訊息內文。

    • 請遵守每個位置的下列速率限制:

      • 每個唯一寄件者 IP 地址和連接埠組合每秒 3 次交易 (TPS)

      • 每個唯一寄件者 IP 地址 1000 TPS

  3. 計算延遲:

    • 將多個 Ping 傳送至每個位置,以計算平均延遲。

    • 請考慮將並行 ping 傳送至多個位置,以加快結果。

    • 視需要使用重試邏輯,為未在短時間內傳回的任何封包傳送新封包 (通常是 1 - 3 秒),因為 UDP 沒有 100% 保證交付。

    • 計算傳送訊息和接收回應之間的時間差異。

    • 如果對某個位置的大部分 UDP ping 一致地沒有得到回應,請使用 ICMP ping 計算標準Amazon GameLift Servers服務端點的延遲作為備用。

提示

無論您在何處記錄玩家在其本機網路上必須開啟的連接埠清單,都建議您包含連接埠 7770。這是另一個原因,因為您應該有後援來測量延遲 (例如使用 ICMP),以防此連接埠遭到封鎖。

程式碼範例

以下是一些簡單的範例,示範如何傳送 UDP ping 和計算延遲。

C++
#include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <chrono> int main() { // Replace with Amazon GameLift Servers UDP ping beacon domain for your desired location const char* domain = "gamelift-ping.ap-south-1.api.aws"; const int port = 7770; const char* message = "Ping"; // Your message const int num_pings = 3; // Number of pings to send // Create socket int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { std::cerr << "Error creating socket" << std::endl; return 1; } // Resolve domain name to IP address struct hostent* host = gethostbyname(domain); if (host == nullptr) { std::cerr << "Error resolving hostname" << std::endl; close(sock); return 1; } // Set up the server address structure struct sockaddr_in server_addr; std::memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); std::memcpy(&server_addr.sin_addr, host->h_addr, host->h_length); // Set socket timeout struct timeval tv; tv.tv_sec = 1; // 1 second timeout tv.tv_usec = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { std::cerr << "Error setting socket timeout" << std::endl; close(sock); return 1; } double total_latency = 0; int successful_pings = 0; for (int i = 0; i < num_pings; ++i) { auto start = std::chrono::high_resolution_clock::now(); // Send the message ssize_t bytes_sent = sendto(sock, message, std::strlen(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (bytes_sent < 0) { std::cerr << "Error sending message" << std::endl; continue; } // Receive response char buffer[1024]; socklen_t server_addr_len = sizeof(server_addr); ssize_t bytes_received = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_addr_len); auto end = std::chrono::high_resolution_clock::now(); if (bytes_received < 0) { std::cerr << "Error receiving response or timeout" << std::endl; } else { std::chrono::duration<double, std::milli> latency = end - start; total_latency += latency.count(); successful_pings++; std::cout << "Received response, latency: " << latency.count() << " ms" << std::endl; } // Wait a bit before next ping usleep(1000000); // 1s } // Close the socket close(sock); if (successful_pings > 0) { double avg_latency = total_latency / successful_pings; std::cout << "Average latency: " << avg_latency << " ms" << std::endl; } else { std::cout << "No successful pings" << std::endl; } return 0; }
C#
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Diagnostics; using System.Threading.Tasks; class UdpLatencyTest { static async Task Main() { // Replace with Amazon GameLift Servers UDP ping beacon domain for your desired location string domain = "gamelift-ping.ap-south-1.api.aws"; int port = 7770; string message = "Ping"; // Your message int numPings = 3; // Number of pings to send int timeoutMs = 1000; // Timeout in milliseconds await MeasureLatency(domain, port, message, numPings, timeoutMs); } static async Task MeasureLatency(string domain, int port, string message, int numPings, int timeoutMs) { using (var udpClient = new UdpClient()) { try { // Resolve domain name to IP address IPAddress[] addresses = await Dns.GetHostAddressesAsync(domain); if (addresses.Length == 0) { Console.WriteLine("Could not resolve domain name."); return; } IPEndPoint endPoint = new IPEndPoint(addresses[0], port); byte[] messageBytes = Encoding.UTF8.GetBytes(message); // Set receive timeout udpClient.Client.ReceiveTimeout = timeoutMs; double totalLatency = 0; int successfulPings = 0; var stopwatch = new Stopwatch(); for (int i = 0; i < numPings; i++) { try { stopwatch.Restart(); // Send message await udpClient.SendAsync(messageBytes, messageBytes.Length, endPoint); // Wait for response UdpReceiveResult result = await ReceiveWithTimeoutAsync(udpClient, timeoutMs); stopwatch.Stop(); double latency = stopwatch.Elapsed.TotalMilliseconds; totalLatency += latency; successfulPings++; string response = Encoding.UTF8.GetString(result.Buffer); Console.WriteLine($"Ping {i + 1}: {latency:F2}ms - Response: {response}"); } catch (SocketException ex) { Console.WriteLine($"Ping {i + 1}: Failed - {ex.Message}"); } catch (TimeoutException) { Console.WriteLine($"Ping {i + 1}: Timeout"); } // Wait before next ping await Task.Delay(1000); // 1s between pings } if (successfulPings > 0) { double averageLatency = totalLatency / successfulPings; Console.WriteLine($"\nSummary:"); Console.WriteLine($"Successful pings: {successfulPings}/{numPings}"); Console.WriteLine($"Average latency: {averageLatency:F2}ms"); } else { Console.WriteLine("\nNo successful pings"); } } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } static async Task<UdpReceiveResult> ReceiveWithTimeoutAsync(UdpClient client, int timeoutMs) { using var cts = new System.Threading.CancellationTokenSource(timeoutMs); try { return await client.ReceiveAsync().WaitAsync(cts.Token); } catch (OperationCanceledException) { throw new TimeoutException("Receive operation timed out"); } } }
Python
import socket import time import statistics from datetime import datetime def udp_ping(host, port, timeout=2): """ Send a UDP ping and return the round trip time in milliseconds. Returns None if timeout occurs. """ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(timeout) message = b'ping' try: # Resolve hostname first try: socket.gethostbyname(host) except socket.gaierror as e: print(f"Could not resolve hostname: {e}") return None start_time = time.time() sock.sendto(message, (host, port)) # Wait for response data, server = sock.recvfrom(1024) end_time = time.time() # Calculate round trip time in milliseconds rtt = (end_time - start_time) * 1000 return rtt except socket.timeout: print(f"Request timed out") return None except Exception as e: print(f"Error: {type(e).__name__}: {e}") return None finally: sock.close() def main(): # Replace with Amazon GameLift Servers UDP ping beacon domain for your desired location host = "gamelift-ping.ap-south-1.api.aws" port = 7770 num_pings = 3 latencies = [] print(f"\nPinging {host}:{port} {num_pings} times...") print(f"Start time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") for i in range(num_pings): print(f"Ping {i+1}:") rtt = udp_ping(host, port) if rtt is not None: print(f"Response from {host}: time={rtt:.2f}ms") latencies.append(rtt) # Wait 1 second between pings if i < num_pings - 1: time.sleep(1) print() # Calculate and display statistics print("-" * 50) print(f"Ping statistics for {host}:") print(f" Packets: Sent = {num_pings}, Received = {len(latencies)}, " f"Lost = {num_pings - len(latencies)} " f"({((num_pings - len(latencies)) / num_pings * 100):.1f}% loss)") if latencies: print("\nRound-trip latency statistics:") print(f" Minimum = {min(latencies):.2f}ms") print(f" Maximum = {max(latencies):.2f}ms") print(f" Average = {statistics.mean(latencies):.2f}ms") print(f"\nEnd time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") if __name__ == "__main__": main()