기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
Amazon GameLift Servers UDP 핑 비콘
UDP 핑 비콘은 플레이어 디바이스와 Amazon GameLift Servers 호스팅 위치 간의 네트워크 지연 시간을 측정할 수 있는 방법을 제공합니다. 핑 비콘을 사용하면 정확한 지연 시간 데이터를 수집하여 게임 서버 배치에 대해 정보에 입각한 결정을 내리고, 지연 시간 요구 사항에 따라 플레이어 매치메이킹을 개선할 수 있습니다.
UDP 핑 비콘 작동 방식
Amazon GameLift Servers는 게임 서버를 배포할 수 있는 각 호스팅 위치에 고정 UDP 엔드포인트(핑 비콘)를 제공합니다. 대부분의 게임 서버는 UDP를 사용하여 통신하므로 UDP 핑 비콘을 사용하여 지연 시간을 측정하면 ICMP 핑을 사용하는 것보다 더 정확한 결과를 얻을 수 있습니다. 네트워크 디바이스는 종종 ICMP 패킷을 UDP 패킷과 다르게 처리하므로 플레이어가 경험할 실제 성능을 반영하지 않는 지연 시간 측정이 발생할 수 있습니다.
UDP 핑 비콘을 사용하면 게임 클라이언트가 이러한 엔드포인트에 UDP 메시지를 보내고 비동기 응답을 수신하여 플레이어의 디바이스와 잠재적 호스팅 위치 간의 실제 게임 트래픽 조건을 더 잘 나타내는 지연 시간 측정값을 제공할 수 있습니다. 엔드포인트는 영구적이며 Amazon GameLift Servers가 해당 위치에서 게임 호스팅을 지원하는 한 계속 사용할 수 있습니다.
UDP 핑 비콘의 일반적인 사용 사례
여러 가지 방법으로 UDP 핑 비콘을 사용하여 게임의 네트워킹 경험을 최적화할 수 있습니다.
최적의 호스팅 위치 선택
다양한 지리적 리전에서 지연 시간 데이터를 수집하여 플레이어 기반에 맞게 게임 서버를 호스팅하는 데 가장 적합한 기본 및 백업 위치를 식별합니다.
플레이어 지연 시간을 기반으로 게임 세션 배치
새 게임 세션을 요청할 때 플레이어 지연 시간 데이터를 포함하면 지연 시간이 가장 짧은 환경을 제공하는 위치를 선택하는 데 도움이 됩니다.
지연 시간을 기반으로 매치메이킹 최적화
매치메이킹을 요청할 때 플레이어 지연 시간 데이터를 제공하여 유사한 지연 시간 프로필을 가진 플레이어를 매칭하고 매치된 플레이어를 위한 최적의 위치에 게임 세션을 배치합니다.
매치메이킹 요청을 생성할 때 플릿이 없는 위치에 지연 시간 정보를 제공해서는 안 됩니다. 이렇게 하면 Amazon GameLift Servers가 플릿 용량이 없는 위치에 게임 세션을 배치하려고 시도하여 매치메이킹 요청에 실패할 수 있습니다.
비콘 엔드포인트 가져오기
Amazon GameLift Servers 위치에 대한 핑 비콘 도메인 및 포트 정보를 검색하려면 ListLocations API 작업을 사용합니다. 이 API에서 반환되는 위치 집합은 호출 시 지정한 AWS 리전(또는 지정하지 않을 경우 기본 리전)에 따라 달라집니다. 다음에서 호출하는 경우:
다중 위치 플릿의 원격 위치일 수만 있는 위치를 사용하여 이 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
}
}
}
각 지연 시간 측정 전에 ListLocations를 호출하는 대신 핑 비콘 정보를 캐시합니다. 도메인 및 포트 정보는 정적이며 API는 대용량 요청을 위해 설계되지 않았습니다.
지연 시간 측정 구현
UDP 핑 비콘을 사용하여 지연 시간 측정을 구현할 때는 다음 모범 사례를 따르세요.
-
다음 방법 중 하나를 사용하여 핑 비콘 정보를 저장합니다.
-
UDP 핑 메시지 전송:
-
지연 시간 계산:
-
각 위치에 여러 핑을 전송하여 평균 지연 시간을 계산합니다.
-
더 빠른 결과를 얻으려면 동시 핑을 여러 위치로 보내는 것이 좋습니다.
-
UDP는 전송을 100% 보장하지 않으므로 필요에 따라 재시도 로직을 사용하여 짧은 시간(일반적으로 1~3초) 내에 반환되지 않은 패킷에 대해 새 패킷을 전송합니다.
-
메시지 전송과 응답 수신 간의 시간 차이를 계산합니다.
-
위치에 대한 UDP 핑의 대부분이 지속적으로 응답을 받지 못하는 경우 표준 Amazon GameLift Servers 서비스 엔드포인트에 대한 ICMP 핑을 대체로 사용하여 지연 시간을 계산합니다.
플레이어가 로컬 네트워크에서 열어야 하는 포트 목록을 문서화하는 위치에 포트 7770을 포함하는 것이 좋습니다. 이는 이 포트가 차단된 경우 지연 시간을 측정하기 위한 대체(예: ICMP 사용)가 있어야 하는 또 다른 이유입니다.
코드 예시
다음은 UDP 핑을 보내고 지연 시간을 계산하는 방법을 보여주는 몇 가지 간단한 예제입니다.
- 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()