IOCP 서버와 연결하기 위해 네트워크 통신을 구현하였다. 여기서는 두 가지 API를 소개하지만 나는 그중에서 Winsock을 사용할 것이다.

1) FSocket

언리얼 엔진에서는 이미 소켓통신을 위한 API를 구비해 두었다. (언리얼 엔진 Socket API)

물론 이 글 이후에 진행되는 모든 것 들은 다 Winsock을 이용할 것이지만 혹시 쓰실 분이 있으실까 해서 남겨놓는다.

(별 다른 이유는 없고 문제가 생기면 찾기 더 쉽고 무엇보다 서버 플머가 만들어준 걸 쓰기 편하기에...)

방법은 Winsock과 크게 다르지 않다.

#include "ClientSocket.h"
#include "SocketSubsystem.h"
#include "Interfaces/IPv4/IPv4Address.h"

FSocket* Socket;	// 소켓 담을 변수를 생성

// 소켓을 생성
Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(TEXT("Stream"), TEXT("Client Socket"));

// IP를 FString으로 입력받아 저장
FString address = TEXT("127.0.0.1");
FIPv4Address ip;
FIPv4Address::Parse(address, ip);

int32 port = 6000;	// 포트는 6000번

// 포트와 소켓을 담는 클래스
TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(ip.Value);
addr->SetPort(port);

GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Trying to connect.")));

// 연결시도, 결과를 받아옴
bool isConnetcted = Socket->Connect(*addr);

 


코드 상세 설명

CreateSocket 함수를 사용해서 FSocket 을 생성한다.

TEXT("Stream")을 주면 TCP 프로토콜을 사용하겠다는 뜻이다. (UDP는 TEXT("DGram")을 인자로 주면 된다.)

두 번째 인자 TEXT("Client Socket") 은 디버그 이름이다. 알기 쉬운 이름으로 지정해주자.

CreateSocket(TEXT("Stream"), TEXT("Client Socket"));

 

FIPv4Address 구조체에 IP 주소를 저장한다.

IP주소는 FString으로부터 Parse함수를 사용하여 저장한다.

FString address = TEXT("127.0.0.1");
FIPv4Address ip;
FIPv4Address::Parse(address, ip);

 

 

FInternetAddr 클래스에 네트워크 정보를 저장한다. 이 클래스에서 네트워크 바이트로 정렬해준다.

TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(ip.Value);
addr->SetPort(port);

 

이제 Connect 함수에 방금 만든 FInternetAddr 클래스의 주소를 보내 연결을 한다.

 

bool isConnetcted = Socket->Connect(*addr);

반환 값을 사용해 접속에 성공했는지 확인해볼 수 있다.


 

2) Winsock

Winsock을 사용한 방법이다. 자세한 설명은 찾아보면 많이 나오니 간단하게 설명하고 넘어가겠다.

// window 기본 타입 Hide
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/prewindowsapi.h"

#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>

#include "Windows/PostWindowsApi.h"
#include "Windows/HideWindowsPlatformTypes.h"

SOCKET Socket;

WSADATA wsaData;
int nRet = WSAStartup(MAKEWORD(2, 2), &wsaData);	// Winsock 초기화
if (nRet != 0) return false;

// 소켓 생성
Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET) return false;

// IP, Port 정보 입력
SOCKADDR_IN stServerAddr;
stServerAddr.sin_family = AF_INET;
stServerAddr.sin_port = htons(6000);
stServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

// 접속
nRet = connect(Socket, (sockaddr*)&stServerAddr, sizeof(sockaddr));
if (nRet == SOCKET_ERROR) return false;

코드 상세 설명

Winsock 라이브러리와 헤더를 추가해주었는데 여기서 아래와 같은 에러가 뜬다. 

Warning C4005 'TEXT': macro redefinition

error C4003: not enough arguments for function-like macro invocation 'min'

Windows.h 헤더를 포함하여 생기는 에러이다.

그래서 에러가 생긴 헤더를 'Windows/AllowWindowsPlatformTypes.h' 로 감싸서 해결해 주었다. (참고)

#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/prewindowsapi.h"

#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>

#include "Windows/PostWindowsApi.h"
#include "Windows/HideWindowsPlatformTypes.h"

 

사용할 Winsock 버전을 선택하고 초기화 하는 과정이다. 반환값을 사용하여 제대로 초기화가 진행 되었는지 확인 할 수 있다.

WSADATA wsaData;
int nRet = WSAStartup(MAKEWORD(2, 2), &wsaData);

 

소켓을 생성한다. AF_INET은 IPv4 주소 체계를, SOCK_STREAM은 TCP 프로토콜을 나타낸다.

이 역시 반환 값을 비교하여 제대로 생성이 되었는 지를 확인 할 수 있다.

Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET) return false;

 

IP주소와 포트번호를 SOCKADDR_IN 구조체에 넣어준다.

SOCKADDR_IN stServerAddr;
stServerAddr.sin_family = AF_INET;
stServerAddr.sin_port = htons(6000);
stServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

 

connect 함수에 SOCKADDR_IN 구조체의 포인터와 크기를 인자로 주면 연결을 시도한다.

nRet = connect(Socket, (sockaddr*)&stServerAddr, sizeof(sockaddr));
if (nRet == SOCKET_ERROR) return false;

 

이렇게 두 가지 방법으로 연결하는 것을 적어보았다.

다음 글에서는 패킷 전송과 수신, 그리고 스레드를 생성하는 것을 적어보겠다.

1) 시작

언리얼 엔진에서 기본으로 제공하는 서버는 보통 소규모 MO 게임을 위한 서버들이다.

(서버 관련 내용은 아래에 잘 정리되어 있으니 한번 찾아보시길)

더보기

하지만 우리들은 IOCP를 사용한 서버를 만들기로 기획하였기에 나는 서버와 통신하는 방법을 이곳저곳 찾아보았다. (아마 이글을 보시는 분들도 대부분 같은 고민을 가지고 찾아오셨을꺼라 생각한다.)

결론은 직접 네트워크 통신을 구현해야 한다는 것인데 소켓 프로그래밍은 3학년 2학기 때 수업에서 배우고 또 직접 개발을 해본 경험이 있기에 괜찮지만 문제는 이걸 엔진과 연동해서 사용해본 적이 없다는 것이다.

많은 시행착오를 거쳐 결국 서버와 연결에 성공했지만 진짜 그 과정에서 참고할 자료가 너무너무너무 없어서 정말 힘들었다.

그래서 조금이나마 도움이 되길 바라며 IOCP 서버와 언리얼 엔진사이의 소켓통신을 구현하는 과정을 공유해 보려고 한다.


(1) FSocket, Winsock 연결

(2) 패킷 Send와 Recv 그리고 스레드 생성

+ Recent posts