상세 컨텐츠

본문 제목

소켓 프로그래밍 기초 #2

똑똑한 개발/C++ 게임개발

by 성댕쓰 2021. 11. 4. 22:54

본문

소켓 client, server를 직접 만들어보자.

 

윈도우 라이브러리를 이용한 방법이다.

DummyClient.cpp

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

int main()
{
	// 윈속 초기화 (ws2_32 라이브러리 초기화)
	// 관련 정보가 wsaData에 채워짐
	WSAData wsaData;
	if (::WSAStartup(MAKEWORD(2, 2), &wsaData))
		return 0;

	// ad: Address Family (AF_INET = IPV4, AF_INET6 = IPv6)
	// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
	// protocol : 0
	// return : descriptor
	SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Socket ErrorCode : " << errCode << endl;
		return 0;
	}

	SOCKADDR_IN serverAddr; // IPv4
	::memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	//serverAddr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
	::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
	serverAddr.sin_port = ::htons(7777);

	// host to network short(호스트에서 네트워크 방식 엔디언으로 바꾸겠다)
	// Little-Endian vs Big-Endian
	// ex) 0x12345678 4바이트 정수
	// low [0x78][0x56][0x34][0x12] high > little
	// low [0x12][0x34][0x56][0x78] hight > big = network표준
	// 확인
	// int16 num = 0x1234;
	// int16 num2 = ::htons(num);

	if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)))
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Socket ErrorCode : " << errCode << endl;
		return 0;
	}

	// ----------------------------
	// 연결 성공! 이제부터 데이터 송수신 가능!

	cout << "Connected to Server!" << endl;
	while (true)
	{
		// TODO

		this_thread::sleep_for(1s);
	}

	// ---------------------------

	// 소켓 리소스 반환
	::closesocket(clientSocket);

	// 윈속 종료
	::WSACleanup();
}

함수 각각을 알아보는 작업도 필요하지만 중요한 것은 큰 흐름을 아는 것이다.

client에서는 socket을 준비하고 목적지를 세팅한 뒤 connect한다.

 

GameServer.cpp

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

int main()
{
	// 윈속 초기화 (ws2_32 라이브러리 초기화)
	// 관련 정보가 wsaData에 채워짐
	WSAData wsaData;
	if (::WSAStartup(MAKEWORD(2, 2), &wsaData))
		return 0;

	// ad: Address Family (AF_INET = IPV4, AF_INET6 = IPv6)
	// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
	// protocol : 0
	// return : descriptor
	SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (listenSocket == INVALID_SOCKET)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Socket ErrorCode : " << errCode << endl;
		return 0;
	}

	// 나의 주소는?
	SOCKADDR_IN serverAddr; // IPv4
	::memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); // <니가 알아서 해줘
	// 위 값 특정하면 해당 주소로만 연결됨. 
	// 만약 네트워크 카드가 여러 개 있어 주소를 여러 개 가지고 있고
	// INADDR_ANY하면 유동적으로 주소를 골라서 연결함.
	serverAddr.sin_port = ::htons(7777);

	// 안내원 폰 개통! 식당의 대표 번호
	if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Socket ErrorCode : " << errCode << endl;
		return 0;
	}

	// 영업 시작!
	// backlog : 대기열의 max count
	if (::listen(listenSocket, 10) == SOCKET_ERROR)
	{
		int32 errCode = ::WSAGetLastError();
		cout << "Socket ErrorCode : " << errCode << endl;
		return 0;
	}

	// ----------------------------
	while (true)
	{
		SOCKADDR_IN clientAddr; // IPv4
		::memset(&clientAddr, 0, sizeof(clientAddr));
		int32 addrLen = sizeof(clientAddr);
		SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
		if (clientSocket == INVALID_SOCKET)
		{
			int32 errCode = ::WSAGetLastError();
			cout << "Socket ErrorCode : " << errCode << endl;
			return 0;
		}

		// 손님 입장!
		char ipAddress[16];
		::inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
		cout << "Client Connected! IP = " << ipAddress << endl;

		// TODO
	}

	// ----------------------------


	// 윈속 종료
	::WSACleanup();
}

소켓 서버는 socket을 준비하고 bind 이용하여 socket을 세팅하고 listen으로 socket 연결을 받는다.

accept에서 대기하다가 client에서 연결이 오면 깨어나서 이후의 작업을 진행한다.

accept반환 값인 SOCKET을 통해 클라이언트와 통신할 수 있다.

 

1. Address Family 중, Ipv4, Ipv6를 의미하는 정의는 각각 무엇?

- Ipv4 : AF_INET, Ipv6 : AF_INET6

2. TCP, UDP를 의미하는 정의는 각각 무엇?

- Tcp : SOCK_STREAM, Udp : SOCK_DGRAM

3. 리틀 앤디안, 빅 앤디안 차이는?

- 리틀 앤디안 : 작은 단위 숫자가 큰 번지의 주소에 저장됨.

- 빅 앤디안 : 리틀 앤디안 반대

4. 클라이언트와 소켓 연결 후, 클라이언트와 통신하기 위해 필요한 값은?

- accept 반환 값인 SOCKET

 

참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)

'똑똑한 개발 > C++ 게임개발' 카테고리의 다른 글

TCP vs UDP  (0) 2021.11.05
TCP서버 실습  (0) 2021.11.05
소켓 프로그래밍 기초 #1  (0) 2021.11.03
TypeCast  (0) 2021.10.29
Object pool  (0) 2021.10.01

관련글 더보기

댓글 영역