

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Amazon GameLift Servers UDP ping ビーコン
<a name="reference-udp-ping-beacons"></a>

UDP ping ビーコンは、プレイヤーデバイスと Amazon GameLift Servers ホスティングロケーション間のネットワークレイテンシーを測定する方法を提供します。ping ビーコンを使用すると、正確なレイテンシーデータを収集して、ゲームサーバーの配置について情報に基づいた意思決定を行い、レイテンシー要件に基づいてプレイヤーのマッチメーキングを改善できます。

## UDP ping ビーコンの仕組み
<a name="reference-how-beacons-work"></a>

Amazon GameLift Servers は、ゲームサーバーをデプロイできる各ホスティング場所に固定 UDP エンドポイント (ping ビーコン) を提供します。ほとんどのゲームサーバーは UDP を使用して通信するため、UDP ping ビーコンでレイテンシーを測定すると、ICMP ping を使用するよりも正確な結果が得られます。ネットワークデバイスは、多くの場合、UDP パケットとは異なる方法で ICMP パケットを処理するため、プレイヤーが経験する実際のパフォーマンスを反映しないレイテンシー測定値につながる可能性があります。

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

## UDP ping ビーコンの一般的なユースケース
<a name="reference-beacons-common-use-cases"></a>

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

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

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

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

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

## ビーコンエンドポイントの取得
<a name="reference-beacons-getting-endpoints"></a>

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

から を呼び出す場合:
+ **マルチロケーションをサポートするフリートのホームリージョン**: API は*すべて*のホスティングロケーションの情報を返します
+ **1 つのロケーションをサポートするフリートのホームリージョン**: API は、そのロケーションの情報を返します。

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

[サポートされている AWS ロケーション](gamelift-regions.md#gamelift-regions-hosting-home) でサポートされているロケーションの表を参照して、単一ロケーションフリートとマルチロケーションフリートをサポートするホームリージョンを特定します。

**例**

```
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 は大量のリクエスト用に設計されていません。

## レイテンシー測定の実装
<a name="reference-beacons-measuring-latency"></a>

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

1. 次のいずれかのアプローチを使用して、ping ビーコン情報を保存します。
   + ゲームクライアントのエンドポイントをハードコード化します。
   + ゲームバックエンドの情報をキャッシュします。
   + 定期的な更新メカニズム (毎日/毎週) を実装して、情報を更新します。

1. UDP ping メッセージの送信:
   + メッセージ本文に必要なものを空でなく、メッセージの最大サイズを 300 バイトに保ちます。
   + ロケーションごとに次のレート制限を確認します。
     + 一意の送信者 IP アドレスとポートの組み合わせごとに 3 トランザクション/秒 (TPS)
     + 一意の送信者 IP アドレスあたり 1000 TPS

1. レイテンシーを計算します。
   + 各ロケーションに複数の ping を送信して、平均レイテンシーを計算します。
   + より迅速な結果を得るには、複数の場所に同時 ping を送信することを検討してください。
   + UDP には 100% の保証された配信がないため、必要に応じて再試行ロジックを使用して、短期間 (通常は 1～3 秒) に返されなかったパケットの新しいパケットを送信します。
   + メッセージの送信とレスポンスの受信の時間差を計算します。
   + ロケーションへの UDP ping の大部分が一貫してレスポンスを返さない場合は、標準[Amazon GameLift Servers サービスエンドポイント](https://docs.aws.amazon.com/general/latest/gr/gamelift.html#gamelift_region)への ICMP ping をフォールバックとして使用してレイテンシーを計算します。

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

## コードの例
<a name="reference-beacons-code-examples"></a>

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

------