728x90
게임 서버
게임 서버를 C++로 만들어보고 있다.
저번에 만들어 본 서버를 토대로 만들다가 너무 지저분하게 코드를 쓰고 있어서 다시 자료를 찾고 재시작중이다. 어렵지만 해야디..해야디.. 게임 서버하고 언리얼, 언리얼 데디서버까지 봐야해서 쉽지는 않을것같다. 그래도 고다고
- C++로 게임 서버를 개발할 때는, 클라이언트와 서버 간의 네트워크 통신을 처리하고, 게임의 상태를 서버에서 관리한다. 서버는 클라이언트의 요청을 받고, 게임 로직을 처리한 후, 그 결과를 다시 클라이언트에 전달한다. 서버는 게임의 중요한 상태를 유지하고, 여러 클라이언트 간에 동기화를 책임진다.
소켓(Socket)과 통신
- C++ 서버는 소켓(Socket)을 이용해 클라이언트와 통신한다. 소켓은 네트워크를 통해 데이터를 주고받을 수 있는 일종의 엔드포인트다. 서버는 소켓을 열어 클라이언트로부터 연결 요청을 받아들이고, 데이터를 주고받을 수 있다. 소켓은 주로 TCP나 UDP 프로토콜을 사용한다.
- TCP (Transmission Control Protocol)
- 신뢰성 있는 연결을 보장하며, 데이터가 순서대로 전달되도록 한다.
- UDP (User Datagram Protocol)
- 빠른 전송이 가능하지만, 데이터가 유실될 수 있고 순서가 보장되지 않는다. 실시간성이 중요한 게임에서는 주로 UDP를 사용한다.
- TCP (Transmission Control Protocol)
기본적인 서버 구조
- 서버는 일반적으로 메인 루프에서 클라이언트의 연결을 기다리고, 클라이언트의 요청을 처리한 후, 해당 데이터를 다른 클라이언트에게 전송하는 구조로 동작한다.
int main()
{
InitializeSocket();
int serverSocket = OpenServerSocket(PORT);
while (true)
{
int clientSocket = AcceptClientConnection(serverSocket);
char buffer[1024];
int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
ProcessGameLogic(buffer);
send(clientSocket, buffer, bytesRead, 0);
}
CloseSocket(serverSocket);
}
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
void InitializeSocket()
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup 실패: %d\\n", result);
exit(1);
}
}
멀티스레딩 (Multi-threading)
- 여러 플레이어가 동시에 접속하는 상황에서는 멀티스레드로 서버를 구현하여 각 클라이언트의 요청을 독립적으로 처리하는 것이 일반적이다. 이를 통해 한 클라이언트의 요청이 다른 클라이언트의 처리에 영향을 미치지 않도록 한다.
void HandleClient(int clientSocket)
{
char buffer[1024];
while (true)
{
int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
ProcessGameLogic(buffer);
send(clientSocket, buffer, bytesRead, 0);
}
}
서버의 역할
- 게임 상태 관리
- 서버는 모든 플레이어의 게임 상태(위치, 점수 등)를 관리하고, 클라이언트 간의 일관성을 유지한다.
- 동기화
- 서버는 각 클라이언트의 행동을 중앙에서 처리하고, 그 결과를 다른 클라이언트에게 전달하여 게임을 동기화한다.
- 보안
- 서버는 게임 로직을 관리하기 때문에, 클라이언트가 임의로 조작할 수 없도록 해야 한다. 따라서 중요한 게임 로직은 서버에서 처리된다.
UE5 데디서버 게임서버 통신
1. 소켓 프로그래밍으로 통신
- 언리얼 엔진의 데디케이티드 서버와 별도의 게임 서버 간에 소켓 프로그래밍을 사용하면, 양방향 통신을 할 수 있다. 양쪽 모두 클라이언트와 서버 역할을 할 수 있고, TCP 또는 UDP 프로토콜을 사용하여 데이터를 주고받을 수 있다.
주요 단계
- 언리얼 데디케이티드 서버에서 소켓 연결: 소켓을 열고, 별도의 게임 서버와 연결.
- 게임 서버에서 소켓 리스닝: 게임 서버에서 소켓을 열어, 언리얼 데디케이티드 서버로부터 연결을 받아들임.
- 데이터 전송: 서로 데이터를 주고받으며, 게임 상태를 동기화하거나 필요한 요청/응답을 처리.
언리얼 엔진에서 소켓 연결을 위한 주요 코드 예시
언리얼 엔진에서 소켓 통신을 구현하려면, FSocket과 ISocketSubsystem을 사용해 네트워크 통신을 할 수 있다.
소켓 클라이언트 (언리얼 서버가 클라이언트 역할):
FSocket* Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
FIPv4Address IP;
FIPv4Address::Parse(TEXT("127.0.0.1"), IP);
TSharedRef<FInternetAddr> Addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
Addr->SetIp(IP.Value);
Addr->SetPort(12345);
bool bConnected = Socket->Connect(*Addr);
if (bConnected) {
FString Message = TEXT("Hello Server!");
int32 Sent;
Socket->Send((uint8*)TCHAR_TO_UTF8(*Message), Message.Len(), Sent);
}
소켓 서버 (별도의 게임 서버)
- 게임 서버는 소켓을 열어 클라이언트(언리얼 서버)로부터 연결을 받아들이고 데이터를 처리한다. 이는 일반적인 C++ 소켓 서버 코드를 참고할 수 있다.
- 여기서는 socket(), bind(), listen(), accept() 등을 사용해 소켓 서버를 구현할 수 있다.
소켓 프로그래밍 장점
- 실시간 통신
- 데이터를 실시간으로 주고받을 수 있어, 빠르고 자유로운 통신이 가능하다.
- 유연한 통신
- 원하는 데이터 형식을 자유롭게 정의할 수 있다.
소켓 프로그래밍 단점
- 구현 복잡성
- 소켓 통신은 상대적으로 구현이 복잡하며, 데이터 전송의 오류 처리나 패킷 관리가 필요하다.
2. HTTP/REST API 통신
- 서버 간의 통신을 좀 더 쉽게 구현하고 싶다면, HTTP/REST API를 통해 통신하는 방법도 있다. 이 방식에서는 별도의 게임 서버가 RESTful 웹 서비스를 제공하고, 언리얼 서버가 이를 호출하여 데이터 요청을 처리할 수 있다.
주요 단계
- 게임 서버에서 REST API 제공
- 게임 서버는 웹 서버를 통해 REST API를 제공한다. 예를 들어, 게임 상태를 가져오거나, 업데이트하는 API 엔드포인트를 생성할 수 있다.
- 언리얼 엔진에서 HTTP 요청 보내기
- 언리얼 엔진의 FHttpModule을 사용해 HTTP 요청을 보내고, 게임 서버에서 데이터를 받는다.
언리얼 엔진에서 HTTP 요청 예시
- 언리얼 엔진에서는 FHttpModule을 사용해 쉽게 HTTP 요청을 보낼 수 있다.
void SendHttpRequest()
{
TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->OnProcessRequestComplete().BindUObject(this, &MyClass::OnResponseReceived);
Request->SetURL(TEXT("<http://localhost:8000/api/gamestate>"));
Request->SetVerb(TEXT("GET"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->ProcessRequest();
}
void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (bWasSuccessful)
{
FString ResponseString = Response->GetContentAsString();
}
}
REST API 장점
- 구현이 상대적으로 간단
- HTTP 프로토콜을 사용하기 때문에, 이미 존재하는 라이브러리나 도구를 쉽게 활용할 수 있다.
- 확장성
- API를 사용해 쉽게 서버 간의 통신을 확장할 수 있다.
REST API 단점
- 실시간 통신 한계
- HTTP 요청/응답 방식은 실시간으로 데이터를 주고받는 데는 한계가 있다.
- 네트워크 오버헤드
- HTTP는 TCP보다 상대적으로 오버헤드가 크기 때문에, 대용량 데이터를 빈번하게 주고받는 경우 성능 이슈가 발생할 수 있다.
결론
- 실시간 통신이 중요하고, 데이터 전송을 빠르게 해야 한다면 소켓 프로그래밍이 더 적합하다.
- 구현이 간단하고, 서버 간에 상태나 설정을 주고받는 정도라면 HTTP/REST API를 사용하는 것도 좋은 선택이다.
728x90
'Study > TIL(Today I Learned)' 카테고리의 다른 글
24.10.14 게임 서버 (0) | 2024.10.14 |
---|---|
24.10.11 게임 서버 (0) | 2024.10.11 |
24.10.08 UE5, CS (8) | 2024.10.08 |
24.10.02 UE5, 알고리즘 (1) | 2024.10.02 |
24.09.30 UE5 (1) | 2024.09.30 |