[Network] Effective Socket Option

녹차·2026년 1월 5일

Network

목록 보기
6/15
post-thumbnail

Introduction

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

지금까지 작성한 모든 응용 프로그램은 소켓의 동작 속성을 변경하지 않고 소켓 함수에 그대로 전달함으로써 구현했습니다. 그러나 다양한 요구사항이 있는 응용 프로그램을 작성하다 보면 같은 소켓 함수를 호출하더라도 소켓 코드나 프로토콜 구현 코드의 동작을 달라져야 할 경우가 있습니다.

이번 글에서는 기존 코드 수정을 최소화할 수 있는 Socket Option에 대해 알아보겠습니다.

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

Body

Socket-Option Concept

소켓 옵션(Socket-Option)이란? Socket의 동작 방식을 세부적으로 제어하기 위해 OS가 제공하는 설정값입니다. 소켓 옵션은 처리 주체에 따라 크게 두 종류로 구분할 수 있습니다

  • 소켓 코드가 처리하는 옵션
    Option을 설정하면 Socket Code에서 해석하고 처리합니다. 프로토콜에 독립적인 성격이 있으나 옵션의 실제 적용 여부는 프로토콜 종류에 다라 달라집니다.

  • 프로토콜 구현 코드가 처리하는 옵션
    Option을 설정하면 프로토콜 구현 코드에서 해석하고 처리합니다. 프로토콜에 의존적인 성격이 있으므로 프로토콜 종류에 따라 옵션 자체가 달라집니다.

소켓 옵션을 설정할 때는 setsockopt() 함수를 사용합니다.

#include<winsock2.h>
int setsocketopt
(
	SOCKET sock,
    int level,
    int optname,
    const char* optval,
    int optlen
)

sock : 옵션을 적용할 대상 소켓입니다.
level : 옵션을 해석하고 처리할 주체를 지시합니다.
optname : 설정할 옵션의 이름입니다.
optnval : 설정할 옵션값을 담고 있는 버퍼의 주소입니다. 옵션값 대부분 정수이지만, 구조체인 경우도 있습니다.
optlen : optval이 가리키는 버퍼의 크기(바이트 단위)입니다.

Socket-Option Type

우선, 소켓 옵션은 그 종류가 매우 다양하기 때문에 이 글에서 모두 다루기는 어렵습니다. 따라서 자세한 목록과 전체 설명은 아래에 관련 링크로 정리해 두었으며, 이번 글에서는 실제 소켓 프로그래밍과 게임 서버 구현 과정에서 자주 사용되는 몇 가지 핵심 옵션만 살펴보겠습니다.

SOL_SOCKET 레벨 옵션(두번 째 인자에 들어감)은 소켓 코드에서 해석하여 처리하므로 프토콜에 독립적인 성격을 띱니다. 하지만 여기에 속한 옵션을 모두 프로토콜에 공통으로 적용할 수 있는 것은 아니므로 주의해야합니다.

SO_SNDBUF & SO_RCVBUF

게임에서 스킬을 연속으로 사용하거나 대규모 이벤트로 인해 접속자 수가 급격히 증가하면, 짧은 시간 동안 네트워크 트래픽이 순간적으로 몰릴 수 있습니다. 이러한 상황에서는 패킷이 한꺼번에 처리되지 못하고 지연되거나 손실되면서, 서버 성능 저하 및 안정성 문제로 이어질 수 있습니다.

이때 운영체제가 소켓에 할당하는 버퍼 크기를 조절함으로써, 순간적인 트래픽 증가에 대한 완충 여력을 확보할 수 있습니다. 이를 위해 사용되는 대표적인 소켓 옵션이 바로 SO_SNDBUFSO_RCVBUF입니다.

  • SO_SNDBUF : 소켓의 송신 버퍼(Send Buffer) 크기를 설정
  • SO_RCVBUF : 소켓의 수신 버퍼(Receive Buffer) 크기를 설정

이 옵션들을 통해 OS가 각 소켓에 할당하는 송신/수신 버퍼의 크기를 조절할 수 있으며, 짧은 시간 동안 데이터가 몰리더라도 즉시 전송 & 수신되지 못한 데이터를 버퍼에 임시로 저장하여 패킷 손실 가능성을 줄일 수 있습니다. 설명 들으면 아래와 같이 생각이 들 수 있습니다.

Q1. 그러면 해당 옵션들을 사용해서 최대로 늘리면 되겠네요!

잠시만요 송신·수신 버퍼를 무작정 크게 설정하는 것은 좋은 방법이 아닙니다.

  • 버퍼 크기를 과도하게 키우면, 데이터가 버퍼에 오래 머물면서 지연 시간이 증가할 수 있고 다수의 소켓을 사용하는 서버에서는 불필요한 메모리 사용으로 인해 오히려 성능이 저하될 수 있습니다. 또한 일정 크기 이상으로 설정하더라도, 운영체제 커널이 내부 정책에 따라 요청 값을 자동으로 조정하는 경우도 있습니다.

  • 반대로 버퍼 크기를 지나치게 작게 설정하면, 순간적인 트래픽 증가를 감당하지 못해 패킷 손실이나 재전송이 발생할 수 있습니다.

그렇기에 일반적으로 수십 KB ~ 수백 KB, 많아도 수 MB 이내의 값을 사용하는 경우가 많습니다.

Q2. 그러면 해당 옵션들은 송수신 전에 해야겠네요?

두 옵션 사용 시 권장 사항은, UDP 소켓은 소켓 버퍼 크기를 언제든지 변경해도 되지만 TCP 소켓은 연결이 이루어지기 전에 변경하는 것입니다.

즉, TCPServer에서는 listen() 함수, TCPClient에서는 connect() 함수 호출 전에 옵션을 설정하는 것이 좋습니다. 또한 TCPServer에 옵션을 적용할 때는, listen_sock에 대해 한 번만 설정해두면 accept() 함수가 리턴하는 client_sock도 자동으로 같은 옵션이 설정된 상태로 생성됩니다.


마지막으로 Code를 살펴보며 SO_SNDBUFSO_RCVBUF 설명을 마치겠습니다.

DWORD optval;
int optlen;
optlen = sizeof(optval);
retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
cout << "수신 버퍼 크기(old) = " << optval << " Byte" << endl;

optval *= 2;

retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&optval, sizeof(optlen));

optlen = sizeof(optval);
retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
cout << "수신 버퍼 크기(new) = " << optval << " Byte" << endl;

SO_REUSEADDR

게임 서버의 핵심 요구 사항 중 하나는 24시간 안정적인 서비스 유지입니다. 하지만 아무리 정교하게 구현된 서버라 하더라도 예기치 못한 버그나 장애로 인해 서버가 종료되는 상황은 발생할 수 있습니다. 이러한 장애가 길어질수록 플레이 경험은 크게 저하되고, 서비스에 대한 신뢰도 역시 떨어지게 됩니다.

따라서 게임 서버는 죽더라도 바로 살아나야되며 서비스 중단 시간을 최소화할 수 있어야 합니다. 이를 위해 사용되는 대표적인 소켓 옵션이 바로 SO_REUSEADDR입니다.

  • SO_REUSEADDR : 이전 연결이 TIME_WAIT 상태에 있더라도 동일한 IP와 포트에 다시 bind()를 허용합니다.

이 옵션을 설정하면, 서버를 종료한 직후 다시 실행하더라도 포트 점유 문제로 인해 서버가 올라오지 않는 상황을 방지할 수 있습니다. 특히 게임 서버처럼 장애 복구, 패치, 재기동이 빈번한 환경에서는 사실상 필수 옵션이라 볼 수 있습니다. 이 옵션은 TCP와 UDP 소켓에 모두 사용할 수 있습니다.

Q1. 그러면 해당 옵션들은 언제 호출해요?

SO_REUSEADDR 옵션 사용 시, TCP & UDP 소켓 모두 반드시 bind() 호출 이전에 설정해야 합니다. 또한 TCPServer에 해당 옵션을 적용할 때는, listen_sock에 대해 한 번만 설정해두면 accept() 함수가 리턴하는 client_sock도 자동으로 같은 옵션이 설정된 상태로 생성됩니다.


마지막으로 Code를 살펴보며 SO_REUSEADDR 설명을 마치겠습니다.

int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));

bind(listen_sock, ...);

SO_KEEPALIVE

TCP 서버는 접속한 클라이언트 수에 비례하여 소켓을 생성합니다. 따라서 이로 인한 시스템 자원 소모도 비례하여 증가하므로 연결이 끊어진 소켓을 그때그때 닫아야 합니다. 그런데 TCP연결의 특성상 연결이 끊어진 사실을 서버가 감지할 수 없는 경우가 발생합니다.

예를 들면 네트워크 케이블 분리, 공유기 강제 종료, 클라이언트 PC 전원 종료 등이 있습니다.

이때 SO_KEEPALIVE 옵션을 설명해두면 TCP 프로토콜 수준에서 상대방의 응답 여부를 확인함으로써, 사실상 끊어진 것으로 판단되는 연결을 자동으로 정리할 수 있습니다.

  • SO_KEEPALIVE : 주기적으로 연결 상태 확인 여부

SO_KEEPALIVE 옵션을 설정하면 TCP 프로토콜 수준에서 연결 여부를 확인하기 위해 상대 TCP에 주기적으로 (기본 2시간 간격) TCP Keep-Alive 패킷을 보냅니다. 이때 상대 TCP의 반응에 따른 동작은 아래와 같습니다.

상대 TCP가 정해진 시간 안에 응답(ACK)하는 경우

TCP 연결에 문제가 없는 상태이므로 응용 프로그램은 정상적으로 동작합니다.

상대 TCP가 정해진 시간 안에 응답하지 않는 경우

TCP연결이 일시적으로 문제가 있는 상태로 간주할 수 있으므로 몇 번 더 TCP패킷을 보내고도 응답이 없다면 자동으로 소켓을 닫아 시스템 자원 소모를 막습니다.

상대 TCP가 RST패킷으로 응답하는 경우

TCP는 예상하지 못한 엉뚱한 패킷이 도달하면 비정상적인 상황으로 간주하여 상대편에 RST 패킷을 보냄으로써 연결을 강제 종료합니다. RST패킷이 도달하면 사실상 연결이 끊어진 것이므로 자동으로 소켓을 닫아 시스템 자원 소모를 막습니다.


SO_KEEPALIVE 옵션에 대한 설명을 보면 아래와 같은 의문이 생길 수 있습니다.

Q1. 게임서버에서 최소 2시간이면 너무 길지 않나요?

좋은 지적입니다. 그렇기에 게임 서버에서는 이 옵션을 단독으로 사용하기보다는, Heartbeat Pattern과 함께 사용하는 경우가 많습니다.

Q2. 그러면 해당 옵션들은 언제 호출하나요

SO_KEEPALIVE 옵션은 TCP 소켓에만 사용할 수 있습니다. TCP 소켓에 사용할 때는 일반적으로 TCP 서버의 listen_sock에 대해 설정하며, 이 경우 accept() 함수가 리턴하는 client_sock도 자동으로 SO_KEEPALIVE 옵션이 설정된 상태로 생성됩니다.


마지막으로 Code를 살펴보며 SO_KEEPALIVE 설명을 마치겠습니다.

DWORD bEnable = 1; // SO_KEEPALIVE 활성화
retval = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&bEnable, sizeof(bEnable));

SO_LINGER

TCP_NODELAY

Conclusion

이번 글에서는 Socket Option의 개념과 분류, 그리고 실제 C++ 게임 서버 구현 과정에서 자주 사용되는 핵심 소켓 옵션들을 중심으로 살펴보았습니다.

소켓 옵션은 단순히 “부가 기능”이 아니라, 서버의 안정성, 성능, 장애 복구 능력을 좌우하는 중요한 요소임을 확인할 수 있었습니다. 이 내용들은 이후 Non-Blocking I/O, Select Model, IOCP 기반 서버 구조를 이해하는 데 중요한 기반이 됩니다.

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

Q1, 소켓 옵션이란 무엇인가?
Q2, 왜 소켓 옵션이 필요한가?

다음 글에서는 두번 째 목표인 IOCP Model을 위해 사전 지식 중 Block & Non-Blcking Socket에 대해 다루겠습니다.

참고문헌

[MS : Socket Option Type]
https://learn.microsoft.com/ko-kr/windows/win32/winsock/sol-socket-socket-options

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

profile
CK23 Game programmer

0개의 댓글