기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
Amazon GameLift Servers UDP ping 비컨
UDP ping 비컨은 플레이어 디바이스와 Amazon GameLift Servers 호스팅 위치 간의 네트워크 지연 시간을 측정하는 방법을 제공합니다. ping 비컨을 사용하면 정확한 지연 시간 데이터를 수집하여 게임 서버 배치에 대해 정보에 입각한 결정을 내리고 지연 시간 요구 사항에 따라 플레이어 매치메이킹을 개선할 수 있습니다.
UDP ping 비컨 작동 방식
Amazon GameLift Servers는 게임 서버를 배포할 수 있는 각 호스팅 위치에 고정 UDP 엔드포인트(ping 비컨)를 제공합니다. 대부분의 게임 서버는 UDP를 사용하여 통신하기 때문에 UDP ping 비컨을 사용하여 지연 시간을 측정하면 ICMP 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는 해당 위치 유형에 서비스 엔드포인트가 없기 때문에 오류를 반환합니다.
단일 및 다중 위치 플릿을 지원하는 홈 리전을 식별지원되는 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
전에를 호출하지 않고 ping 비컨 정보를 캐시합니다. 도메인 및 포트 정보는 정적이며 API는 대용량 요청을 위해 설계되지 않았습니다.
지연 시간 측정 구현
UDP ping 비컨을 사용하여 지연 시간 측정을 구현할 때는 다음 모범 사례를 따르세요.
-
다음 방법 중 하나를 사용하여 ping 비컨 정보를 저장합니다.
-
UDP ping 메시지 전송:
-
지연 시간 계산:
-
각 위치에 여러 ping을 전송하여 평균 지연 시간을 계산합니다.
-
더 빠른 결과를 얻으려면 동시 ping을 여러 위치로 보내는 것이 좋습니다.
-
UDP는 전송을 100% 보장하지 않으므로 필요에 따라 재시도 로직을 사용하여 짧은 시간(일반적으로 1~3초) 내에 반환되지 않은 패킷에 대해 새 패킷을 전송합니다.
-
메시지 전송과 응답 수신 간의 시간 차이를 계산합니다.
-
위치에 대한 UDP ping의 대부분이 지속적으로 응답을 받지 못하는 경우 표준 Amazon GameLift Servers 서비스 엔드포인트에 대한 ICMP ping을 대체로 사용하여 지연 시간을 계산합니다.
플레이어가 로컬 네트워크에서 열어야 하는 포트 목록을 문서화하는 위치에 포트 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()