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 を使用して通信するため、UDP ping ビーコンでレイテンシーを測定すると、ICMP ping を使用するよりも正確な結果が得られます。ネットワークデバイスは、多くの場合、UDP パケットとは異なる方法で ICMP パケットを処理するため、プレイヤーが経験する実際のパフォーマンスを反映しないレイテンシー測定値につながる可能性があります。

UDP ping ビーコンを使用すると、ゲームクライアントは UDP メッセージをこれらのエンドポイントに送信し、非同期レスポンスを受け取ることができます。これにより、プレイヤーのデバイスと潜在的なホスティング場所間の実際のゲームトラフィック状態をより適切に表すレイテンシー測定値が得られます。エンドポイントは永続的であり、 がその場所でゲームホスティングAmazon GameLift Serversをサポートしている限り、引き続き使用できます。

UDP ping ビーコンの一般的なユースケース

UDP ping ビーコンをいくつかの方法で使用して、ゲームのネットワークエクスペリエンスを最適化できます。

最適なホスティングロケーションの選択

さまざまな地理的リージョンでレイテンシーデータを収集して、プレイヤーベースのゲームサーバーをホストするための最適なプライマリロケーションとバックアップロケーションを特定します。

プレイヤーのレイテンシーに基づくゲームセッションの配置

新しいゲームセッションをリクエストするときにプレイヤーのレイテンシーデータを含め、レイテンシーが最も低い場所を選択するのに役立ちます。

レイテンシーに基づくマッチメーキングの最適化

同様のレイテンシープロファイルを持つプレイヤーをマッチングし、マッチングされたプレイヤーに最適な場所にゲームセッションを配置できるように、マッチメーキングをリクエストするときにプレイヤーレイテンシーデータを提供します。

注記

マッチメーキングリクエストを作成するときは、フリートがない場所にレイテンシー情報を提供しないでください。その場合、 はフリート容量がない場所にゲームセッションを配置しようとし、マッチメーキングリクエストが失敗Amazon GameLift Serversする可能性があります。

ビーコンエンドポイントの取得

Amazon GameLift Servers ロケーションの ping ビーコンドメインとポート情報を取得するには、ListLocations API オペレーションを使用します。この API によって返される場所のセットは、呼び出し時に AWS リージョン 指定する (指定しない場合はデフォルトのリージョン) によって異なります。から を呼び出す場合:

  • マルチロケーションをサポートするフリートのホームリージョン: API はすべてのホスティングロケーションの情報を返します

  • 1 つのロケーションをサポートするフリートのホームリージョン: API は、そのロケーションの情報を返します。

マルチロケーションフリートのリモートロケーションのみが可能なロケーションを使用してこの API を呼び出すと、そのタイプのロケーションにはサービスエンドポイントがないため、API はエラーを返します。

でサポートされているロケーションの表を参照してサポートされている AWS ロケーション、単一ロケーションフリートとマルチロケーションフリートをサポートするホームリージョンを特定します。

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

これにより、マルチロケーションフリート AWS リージョン がサポートされるため、複数のロケーションが返されます。戻り値の 1 つの例を次に示します。

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

各レイテンシー測定ListLocationsの前に を呼び出すのではなく、ping ビーコン情報をキャッシュします。ドメインとポートの情報は静的であり、API は大量のリクエスト用に設計されていません。

レイテンシー測定の実装

UDP ping ビーコンを使用してレイテンシー測定を実装する場合は、次のベストプラクティスに従ってください。

  1. 次のいずれかのアプローチを使用して、ping ビーコン情報を保存します。

    • ゲームクライアントのエンドポイントをハードコードします。

    • ゲームバックエンドの情報をキャッシュします。

    • 定期的な更新メカニズム (毎日/毎週) を実装して、情報を更新します。

  2. UDP ping メッセージの送信:

    • メッセージ本文に必要なものを空でなく、メッセージの最大サイズを 300 バイトに保ちます。

    • ロケーションごとに次のレート制限を確認します。

      • 一意の送信者 IP アドレスとポートの組み合わせごとに 3 トランザクション/秒 (TPS)

      • 一意の送信者 IP アドレスあたり 1000 TPS

  3. レイテンシーを計算します。

    • 各ロケーションに複数の ping を送信して、平均レイテンシーを計算します。

    • より迅速な結果を得るには、複数の場所に同時 ping を送信することを検討してください。

    • UDP には 100% の保証された配信がないため、必要に応じて再試行ロジックを使用して、短期間 (通常は 1~3 秒) に返されなかったパケットの新しいパケットを送信します。

    • メッセージの送信とレスポンスの受信の時間差を計算します。

    • ロケーションへの UDP ping の大部分が一貫してレスポンスを返さない場合は、標準Amazon GameLift Serversサービスエンドポイントへの ICMP ping をフォールバックとして使用してレイテンシーを計算します。

ヒント

プレイヤーがローカルネットワークで開いている必要があるポートのリストを文書化する場合は、ポート 7770 を含めることをお勧めします。これは、このポートがブロックされた場合に (ICMP などを使用して) レイテンシーを測定するフォールバックが必要なもう 1 つの理由です。

コードの例

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()