언리얼 엔진 소켓 프로그래밍 (1) FSocket, Winsock 연결

Kclient·2022년 10월 19일
1

UnrealEngine

목록 보기
1/5

언리얼 엔진에서는 기본적으로 네트워크 연결을 지원해준다. 하지만 WinAPI로 소켓프로그래밍을 하여 직접 네트워크 연결을 구현해보자.

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

1. FSocket

언리얼 엔진에서는 이미 소켓통신을 위한 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);

1.1 코드 상세 설명

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;

2.2 코드 상세 설명

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;

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

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

profile
뭐든 손에 잡히는 대로 해보자

0개의 댓글