翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
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 を呼び出すと、そのタイプのロケーションにはサービスエンドポイントがないため、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 ビーコンを使用してレイテンシー測定を実装する場合は、次のベストプラクティスに従ってください。
-
次のいずれかのアプローチを使用して、ping ビーコン情報を保存します。
-
UDP ping メッセージの送信:
-
レイテンシーを計算します。
-
各ロケーションに複数の 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()