2025. 12. 2. 12:23ㆍGame/Web and Game Server
※ Reference
- 개복치를 위한 CPP 프로그래밍, 본인 제작(2025/인프런)
- 게임 서버 프로그래밍 교과서, 배현직 지음(2019/길벗)
- Computer Networking : A Top-down Approach, 8th edition by Jim kurose/Keitrh Ross
※ 안사항
- 본 게시글을 읽으실때 Computer Network와 CPP에 대한 이해가 충분하지 않으시다면 많이 힘드실 수 있습니다.
- Computer Networking : A Top-down Approach의 경우 PPT가 공개되어 있어 해당 자료를 사용하나, 나머지 자료화면들은 아닌 경우가 있습니다. 물론 제가 만든거면 딱히 뭐라 안합니다만..
- ChatGPT/Gemeni/Claude의 도움을 받으시면 이해하기 더욱 쉬우나, Hallucination에 유의해 주세요. GPT의 경우 CODEX를 이용하시면 더욱 훌륭한 결과물을 얻을 수 있습니다.

Non-Blocking Socket을 하기 전에, 일단 아주아주아주 간단한 클라이언트쪽 예제를 한번 만들어 봅시다. 그러기 위해서 위대하신 마이크로소프트 공식 매뉴얼을 찾아보니 - Winsocket을 초기화 -> 소켓생성 -> 바인딩 -> 수신 대기 -> 연결 수락 -> 데이터 이동 과 같은 절차를 밟으라고 명하고 있네요. Connect, Send, Recv와 같은 동작이 필요할 것으로 보이니 헤더에 좀 적어봅시다.
// Socket.h
#include <iostream>
#include <string>
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32")
#define _WIN32_WINNT 0x0601
#define DEFAULT_PORT "4444" // 프로그램에서 사용할 기초 포트
#define DEFAULT_BUFLEN 512
class ClientSocket {
public:
struct addrinfo* result = nullptr, * ptr = nullptr, hints; // 주소 정보 등에 관한 구조체 생성
SOCKET ConnectSocket;
ClientSocket(std::string) {};
virtual void Connect();
virtual void Send(char*);
virtual void Recv(char*);
virtual void end();
};
왠지 C언어 구문이 좀 많이 보이는것 같습니다만, CPP를 할줄 알면 Clang은 어느?정도 읽을 수 있으므로 넘어가 주도록 합시다. 이후로는 함수들을 아래와 같이 구현해 주면 됩니다. (동작 하는지는 모르겠는???데 그냥 저런식이구나 하고 이해해 주시면 감사하겠습니다.)
#include "Socket.h"
using namespace std;
ClientSocket::ClientSocket(string adress) {
//addrinfo 개체 생성
ZeroMemory(&hints, sizeof(hints)); // 메모리 밀기
hints.ai_family = AF_UNSPEC; // IPv4던 IPv6던 딱히 지정은 안함
hints.ai_socktype = SOCK_STREAM; // OOB Byte Stream(TCP)
hints.ai_protocol = IPPROTO_TCP; // 프로토콜은 TCP
int iresult = getaddrinfo(adress.c_str(), DEFAULT_PORT, &hints, &result); // 주소에서 정보 받아오기
if (iresult != 0) {
WSACleanup();
}
ConnectSocket = INVALID_SOCKET; // 일단은 비워두기
ptr = result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); // 소켓 생성
if (ConnectSocket == INVALID_SOCKET) {
freeaddrinfo(result);
WSACleanup();
}
};
void ClientSocket::Connect() {
int iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); // 소켓 연결
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
freeaddrinfo(result);
}
if (ConnectSocket = INVALID_SOCKET) {
WSACleanup();
cout << "Unable to connect!" << endl;
}
};
void ClientSocket::Send(char* sendbuf) {
int iresult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iresult == SOCKET_ERROR) {
closesocket(ConnectSocket);
WSACleanup();
cout << "Send failed" << endl;
}
else cout << "Sent!" << endl;
};
void ClientSocket::end() {
int iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
WSACleanup();
cout << "Shoutdown failed" << endl;
}
}
void ClientSocket::Recv(char* recvbuf) {
int iResult;
do {
iResult = recv(ConnectSocket, recvbuf, 512, 0); // 받은 바이트의 정수값 반환
if (iResult > 0) cout << "Bytes Received : " << iResult << endl;
else if (iResult == 0) cout << "Connection Closed" << endl;
else cout << "recv failed" << endl;
} while (iResult > 0);
}
모든 라이브러리가 대게 그렇지만 - 뭔가 우리가 모르는 Struct들이 많아서 머리가 아플것 같습니다. 하지만 위대한 마이크로소프트는 친히 모든 설명을 메뉴얼에 적어 두었습니다! 그래서 간단하게 Connect/Send/End/Recv 동작을 만들어 보았습니다. 사실 bind()도 만들어서 포트를 따로 묶을까 고민했습니다만.. 뭐 어차피 예시용인데요.
쨋든 문제는 위와 같은 상황에서는 블로킹이 난다는 겁니다. 수신하는 쪽에서 송신 속도를 못따라가면 송신 버퍼도 언젠가는 가득 차서 블로킹이 날 수 있겠죠. 그래서 Non-Block Socket API가 있습니다!

논블록으로 소켓을 바꾸면, 리턴값이 좀 기기괴괴해집니다. 이 점은 메뉴얼을 참고해서 넘어가주면, 이제 블로킹이 일어날 상황이면 오류 코드를 띄우고 그냥 넘어가집니다! 그리고 한 스레드에서 소켓 여러개를 다룰 수 있게 됩니다. 루프를 돌면서 - 데이터가 있으면 받아오고, 없으면 그냥 즉시 리턴됩니다. 블로킹 소켓을 쓰는 상황이었으면, 한 소켓이 블로킹 당했으면 그걸 기다리느라 다른 얘들을 처리하지도 못했을 겁니다. 다만 스레드에서 루프를 계속 돌아주는 상황이 나와 CPU가 비명을 지를 수 있습니다. 이때는 polling이라는 훌륭한 수단이 있음을 인지하시고, OS 라이브러리를 찾아보면 함수가 분명 있을겁니다.
그리고 송신/수신 할때를 빼고는 논블로킹을 사용하면 좀 거시기 합니다. 이때는 0바이트를 송신해서 이놈이 죽어있나 살아있나 체크해주고 적절한 로직으로 처리해 주면 됩니다.
아, 그렇다고 논블로킹 소켓이 (특히 UDP에서) 만능은 아닙니다. 괜히 Async I/O가 있는게 아닙니다. 더불어 이것도 만능은 아니라서 나중에는 IOCP/epoll을 가져다 씁니다. 이에 대해서는 다음 시간에 얘기합시다...
'Game > Web and Game Server' 카테고리의 다른 글
| [CPP] Socket Programming(1) (0) | 2025.12.01 |
|---|