[Network] Socket Programming - Concopt, Open & Close

녹차·2025년 12월 30일

Network

목록 보기
2/15
post-thumbnail

Introduction

C++ 게임 서버를 개발하기 위해서는 소켓 프로그래밍(Socket Programming)에 대한 이해가 필수적입니다. 그리고 소켓 프로그래밍을 제대로 다루기 위해서는, 그 기반이 되는 네트워크(Network)운영체제(OS)에 대한 기본적인 지식이 선행되어야 합니다.

첫 번째 목표는 Socket Open & Close의 정확한 이해입니다. 단순히 코드만 나열하는 것이 아니라, 그 안에 내포된 OS와 네트워크 개념을 함께 이해해야 각 코드가 왜 필요한지, 어떤 의미를 가지는지를 명확히 알 수 있습니다.

이 글은 Programming 관점의 Socket대한 이해와 본격적인 Socket Open & Close Programming까지 하나씩 살펴봅니다.

[학습목표]
1. Programming 관점에서 바라본 Socket이란?
2. Socket Open & Close Programming

(이 글은 여러 기술 문서와 교재 등 다양한 자료를 참고하였습니다)

Body

Socket에 대해 아래와 같이 설명한 적이 있습니다. 이는 Socket 개념적 정의에 해당합니다. 하지만 Socket Programming에서 Socket은 중요도 높기에 구현적 정의에 대해서도 자세히 다뤄보겠습니다.

[Network] Socket Programming - IP, Port Number, Socket Address Struct 일부
일반적으로 네트워크 통신에서 IP 주소, 전송 계층 프로토콜(TCP/UDP), 포트 번호의 조합을 통해 통신의 한 종단점을 식별하며, 이를 흔히 소켓(Socket)이라 부른다.

Socket's history

소켓(Socket)은 Network Programming을 위한 interface로 버클리 대학(University of California at Berkeley)에서 개발되어 1982년 BSD(Berkeley Software Distribution) UNIX 4.1에서 처음 소개되었습니다. 이후 1986년 BSD UNIX 4.3에서 개정된 형태가 현재까지 널리 사용되고 있습니다.

이 소켓 인터페이스를 Windows 환경에서도 사용할 수 있도록 확장한 것이 Windows Sockets(WinSock) 이며, Windows 95부터 운영체제의 API로 정식 포함되어 제공되고 있습니다.

Socket's Concept

네트워크 통신을 수행하기 위해서는 하드웨어의 신호 처리, 장치 드라이버를 통한 하드웨어 제어, OS 차원의 프로토콜 구현 등 여러 계층이 유기적으로 협력해야 합니다.

하지만 네트워크 프로그램을 개발하는 입장에서 이러한 모든 내부 동작을 깊이 이해할 필요는 없습니다. Socket은 이러한 복잡한 과정을 추상화하여 제공하므로, 개발자는 데이터를 초기화하고 정해진 절차에 따라 소켓 관련 함수들을 호출하는 것만으로도 네트워크 통신 프로그램을 구현할 수 있습니다.

즉 프로그래밍 관점애서 소켓이란? Network을 통해 Data를 주고받기 위해 OS가 제공하는 Interface라고 할 수 있습니다.

Socket Porgramming에서 'Socket'이라는 용어의 의미를 Data Type, Communication End-Point(통신 종단점), 네트워크 프로그래밍 인터페이스 등 세 가지 관점에서 알아볼 것입니다.

Data Type

Socket은 window-Programming에 사용되는 Handle과 유사한 개념입니다. 일단 만들고 나면 함수를 호출하여 손쉅게 네트워크 통신을 할 수 있습니다. 응용 프로그램이 통신하려면 다음과 같은 요소가 결정되어야 합니다.

  • 사용할 프로토콜(TCP/IP, UDP/IP)
  • 송신 측 IP Address, Port Number
  • 수신 측 IP Address, Port Number

이와 같은 정보는 OS에서 내부적으로 관리하며, 응용 프로그램은 Socket을 사용하여 이 정보에 접근할 수 있습니다.

Socket은 Programming 관점에서 데이터 타입으로 볼 수 있는데, 생성과 설정 과정이 끝나면 운영체제의 통신 관련 정보를 참조하여 다양한 작업을 편리하게 할 수 있습니다.

Communication End-Point

Socket은 응용 프로그램 관점에서 통신 종단점 즉, 통신의 출발점과 도착점이라고 볼 수 있습니다.

Client는 자신의 Socket이 Server Socket과 연결된 것으로 생각하고 send() 함수를 호출하여 데이터를 보낸니다. Server도 자신의 Socket이 Client Soket과 연결된 것으로 생각하고 recv() 함수를 호출하여 데이터를 받습니다. Server가 Client에 data를 보내는 경우도 같은 원리로 생각할 수 있습니다.

  • Client : 서비스를 요청하는 쪽
  • Server : 클라이언트가 요청한 서비를 처리하는 쪽

Network Programming Interface

Socket은 TCP/IP Protcol 관점에서 Network Programming interface에 불과합니다. 따라서 응용 프로그램이 통신하기 위해서 양쪽 모두 Socket을 사용해야 하는 것은 아닙니다. 둘 다 같은 Protocol을 사용하고, 정해진 형식과 절차에 따라 Data을 주고받으면 됩니다.

Soket Interface을 사용하여 TCP나 UDP Protocol에 접근할 수 있으며, 필요시 전송 계층을 건너뛰고 곧바로 ICMP, IGMP, IP을 직접 사용할 수 있습니다.

Soket's Characteristics

Windows Socket(WinSock)은 UNIX Socket을 기반으로 설계된 네트워크 프로그래밍 인터페이스이기 때문에, 소스 코드 수준에서 비교적 높은 호환성을 가집니다.

하지만 OS 환경과 내부 구현 방식의 차이로 인해, 기존 UNIX Socket과는 일부 동작 방식과 사용법에서 차이점이 존재합니다.

  • Window Socket은 DLL(Dynamic-Link Libary)을 통해 대부분의 기능이 제공되므로 DLL 초기화와 종료 작업을 위한 원도우 전용 함수가 필요합니다.

  • Window Program은 대게 GUI를 갖추고 메시지 구동 방식으로 동작하므로 이를 위한 확장 함수가 존재합니다.

  • Window는 OS차원에서 Multithread를 지원하므로 안정적으로 동작하기 위한 구조와 이를 위한 함수가 필요합니다.

Soket Programming's Pros

  • Window와 Linux Socket은 소스 코드 수준에서 호환성이 높으므로 기존 코드를 이식하여 활용하기 쉅습니다.

  • 가장 널리 사용하므로 한번 배워두면 여러 OS에서 사용할 수 있습니다.

  • TCP/IP 외에 프로토콜도 지원하는 최소 코드 수정으로 프로토콜을 변경할 수 있습니다.

  • 저수준 프로그래밍 인터페이스이므로 세부 제어가 가능하여 고성능 네트워크 프로그램을 개발할 수 있습니다.

Socket Programming's cons

  • 프로그래머가 응용 프로그램 수준의 프로토콜을 직접 설계해야 합니다. 즉 주고받는 데이터 형식이나 전송 절차 등을 정해서 프로그래밍해야 하며, 설계 변경 시에는 코드를 수정해야 합니다.

  • 서로 다른 Byte Ordering를 사용하거나 데이터 처리 단위(32bit, 64bit)가 다른 Host끼리 통신할 경우, 응용 프로그램 수준에서 데이터 변환을 처리해야 합니다.

Socket's Structure

Window OS는 윈속 응용 프로그램을 위해 아래와 같은 구조를 제공합니다. 원속이 지원하는 대부분 기능은 WS2_32.DLL로 제공되며, 원속 1.x 응용 프로그램은 WSOCK32.DLL을 통해 궁극적으로 WS2_32.DLL을 사용합니다. 표준 윈속 API외에도 MS에서 확장한 API가 존재하며 MSWSOCK.DLL로 제공됩니다.

WS2_32.DLL은 응용 프로그램이 실제로 사용할 하부 프로토콜을 선택하여 연결해줍니다. C++에서는 C++ Standard Library가 아니라 Window API Libary이기에, .lib 파일은 링커 단계에서 사용되므로 #include로 불러올 수 없어서 아래와 같이 사용합니다.

#pragma comment(lib, "ws2_32.lib") // 윈속 라이브러리(ws2_32.lib) 같이 링크해줘!

WS2_32.DLL의 경로는 Windows/System32/WS2_32.DLL입니다.

Socket Programming Open/Close

본격적인 코드에 앞서 window에서는 socket 생성 전에 원속 초기화와 소켓 닫기 후에 원속 종료 단계가 더 필요합니다. 만약 윈속 초기화하는 WSAStartup() 함수가 실패하면 WS2_32.DLL이 메모리에 Load되지 않습니다.

WSA는 Window Soket Application의 약자입니다.

1. #pragma comment(lib, "ws2_32.lib") // 윈속 라이브러리2.(ws2_32.lib) 같이 링크해줘!
2.
3. #include <winsock2.h> // 윈속2 메인 헤더
4. #include <ws2tcpip.h> // 윈속2 확장 헤더
5. #include <iostream>
6.
7. using namespace std;
8. 
9. int main()
10. {
11.	 //#윈속 초기화
12.	 WSADATA wsa;
13.	 if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
14.		 return 1;
15.
16.	 cout << "[알림] 윈속 초기화 성공" << endl;
17.
18.	 //#소켓 생성
19.	 SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
20.	 cout << "[알림] 윈속 생성 성공" << endl;
21.
22.	 //#소켓 닫기
23.	 closesocket(sock);
24.
25.	 //#윈속 종료
26.	 WSACleanup();
27.
28.	 return 0;
29. }

먼저 전처리기(#include)부분부터 중요한 점은 관례 수준이 아닌 헤더 내부 의존성 때문에 해당 순서를 반드시 지켜야합니다. 만일 거꾸로 한다면 컴파일 애러, 중복 정의 등 문제가 될 수 있습니다.

Q1, 왜 꼭 순서를 지켜야 하나요?

ws2tcpip.hwinsock2.h에 의존하기 때문입니다. ws2tcpip.h는 타입과 구조체가 이미 winsock2.h에 정의되어 있다고 가정하고 만들어졌기에 순서를 지켜줘야 합니다.

13번째, 26번째를 보면 소캣을 생성 이전과 닫기 후에 각각 원속 초기화와 원속 종료를 해주었습니다. WSACleanup();은 굳이 설명안해도 알 것이고, 13번째 코드에 대해 한마디로 정리하면 아래와 같습니다.

WinSock 라이브러리를 사용하기 전에 초기화를 시도하고, 실패하면 프로그램을 종료해줘!

이때 중요한 점은 MAKEWORD(2, 2)입니다. MAKEWORD(2,2)는 WinSock 2.2 Version을 사용하겠다고 요청하는 것입니다. 앞에 있는 인자를 Major version, 뒤에 있는 인자를 Minor version이라고 합니다.

마지막 19번째 소켓 생성을 설명하기 이전 주소 체계와 소켓 타입에 대한 설명을 하겠습니다.

주소 체계(Address Family)란?

통신하려면 통신 상대를 유일하게 지정할 수 있는 주소가 필요합니다. 주소 체계(Address Family)란 이런 주소 지정 방식을 나타내는 값입니다. 주소 체계는 네트워크 프로토콜의 종류에 따라 달라집니다.

아래는 주소 체계(Address Family)를 나타내는 대표적인 상숫값이며, 자신이 사용할 프로토콜에 대응하는 값을 선택하며 soket() 함수의 첫 번째 인수로 전달하면 됩니다.

IPv4와 IPv6은 아래 글을 참고하시면 됩니다.
https://velog.io/@whdrnr545/Network-Socket-Programming-IP-Port-Number-Socket-Address-Struct#%EC%B0%B8%EA%B3%A0%EB%AC%B8%ED%97%8C

소켓 타입(Soket Type)이란?

소켓 타입(Socket Type)은 사용할 protocol의 특성을 나타내는 값입니다. 자주 사용되는 Socket Type은 아래와 같으며 자신이 사용할 protocol에 대응하는 값을 선택하며, soket() 함수의 두 번째 인수로 전달하면 됩니다.


socket()의 세번 째 인자는 Protocol입니다. 같은 주소 체계와 소켓 타입만으로 protocol을 결정할 수 있는 예도 있으나 일반적으로는 주소 체계소켓 타입이 같아도 이에 해당하는 protocol이 두 개 이상 존재합니다.

이때는 명시적으로 지정해야 합니다. 하지만 주로 다룰 TCP, UDP protocol은 주소 체계와 소켓 타입만으로 protocol을 결정할 수 있기에 0을 넣습니다.

socket()의 세 개의 인자에 대한 이해를 하고 다시 코드를 보면 IPvi4, TCP을 생성한 것을 알 수 있습니다.

Conclusion

이번 글에서는 C++ 기반 게임 서버 개발을 위한 첫 단계로서, 소켓 프로그래밍의 기본 개념과 구조, 그리고 Windows 환경에서의 WinSock 사용 방식을 간단한 코드 예제와 함께 살펴보았습니다.

Socket은 네트워크 통신의 복잡한 내부 동작을 운영체제 수준에서 추상화한 인터페이스로, 개발자는 이를 통해 TCP/IP와 같은 프로토콜을 직접 다루지 않고도 네트워크 프로그램을 구현할 수 있습니다. 또한 소켓을 데이터 타입, 통신 종단점, 네트워크 프로그래밍 인터페이스라는 세 가지 관점에서 이해함으로써, 네트워크 통신에서 소켓이 어떤 역할을 수행하는지 명확히 정리할 수 있었습니다.

아울러 Windows Socket이 UNIX Socket을 기반으로 설계되었기 때문에 높은 코드 호환성을 가지지만, DLL 기반 구조와 초기화 과정 등 Windows 환경에 특화된 차이점이 존재함도 확인했습니다. 이를 통해 WinSock을 사용할 때 반드시 고려해야 할 초기화(WSAStartup)와 종료(WSACleanup)의 중요성, 그리고 헤더 포함 순서와 라이브러리 링크 방식에 대해서도 이해할 수 있었습니다.

글을 제대로 읽으셨다면 아래의 질문들을 제대로 답하실 수 있을겁니다.

Q1. 프로그래밍 관점에서 Socket이란 무엇이며, 왜 OS가 제공하는 인터페이스라고 할 수 있나요?
Q2. Socket-Programming의 장단점은 무엇인가요?
Q3. C++에서 #pragma comment(lib, "ws2_32.lib")가 필요한 이유는 무엇인가요?
Q4. 아래와 같이 순서를 지켜야하는 이유는 무엇인가요?

1. #include <winsock2.h> // 윈속2 메인 헤더
2. #include <ws2tcpip.h> // 윈속2 확장 헤더
Q5. 주소체계은 무엇인가요?
Q6. Socket Type은 무엇인가요?

다음 글에서는 TCP/IP의 개념과 Server & Client를 직접 구현해보며 두 번째 목표를 달성할겁니다.

참고문헌

[교보문구]
https://product.kyobobook.co.kr/detail/S000061352062
https://product.kyobobook.co.kr/detail/S000001792817

profile
CK23 Game programmer

0개의 댓글