나만의 작은 도서관
[C++] 게임서버 탐구 일지 #4. 다양한 소켓 옵션 본문
목차
- 소켓 옵션 get, set
- SO_KEEPALIVE
- SO_LINGER
- ::shutdown()
- SO_SNDBUF, SO_RCVBUF
- SO_REUSERADDR
- TCP_NODELAY
소켓 옵션 get, set
소켓 옵션은 클라이언트-서버가 통신할 때 발생하는 특정 상황을 어떻게 대응할 것인지 설정하기위해 사용한다. 설정된 옵션을 가져오기 위해서는 getsockopt() 함수를, 옵션을 설정하기 위해선 setsockopt() 함수를 사용하면된다.
getsockopt() 함수와 setsockopt() 함수는 동일한 인자를 받으며 총 5개의 인자를 넣어줘야한다. setsockopt() 함수를 보면 다음과 같이 생겼다.
int setsockopt(
[in] SOCKET s,
[in] int level,
[in] int optname,
[in] const char *optval,
[in] int optlen
);
- s: 옵션을 설정할 소켓
- level: 옵션을 해석하고 처리할 주체. 주체로는 SOL_SOCKET(소켓 코드), IPPROTO_IP(IPv4), IPPROTO_TCP (TCP) 등이 있다.
- optname: 옵션의 이름
- optVal: 옵션의 설정값
- optlen: optVal의 크기. sizeof(optVal)와 같이 넣어주면 된다.
소켓 옵션 링크
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock/nf-winsock-setsockopt
SO_KEEPALIVE
통신을 하다보면 상대가 소리소문 없이 연결을 끊는 상황이 발생할 수 있다. SO_KEEPALIVE는 이러한 상황을 감지하기 위해 주기적으로 데이터를 보내 상대가 살아있는 지 확인한다.
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET에 SO_KEEPALIVE를 적용하는 코드이다.
bool enable = true;
::setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&enable, sizeof(enable));
SO_LINGER
통신을 종료하는 상황에 미처 보내지 못한 데이터가 송신 버퍼에 남아있을 수 있다. 이 때 아직 못 보낸 데이터를 마저 보낼 것인지, 버릴 것인지 선택하기 위한 옵션이 바로 SO_LINGER이다.
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET에 SO_LINGER를 적용하는 코드이다.
// LINGER 생성
LINGER linger;
linger.l_onoff = 1; // 1은 on, 0은 off
linger.l_linger = 5; // 대기 시간
::setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
::shutdown()
shutdown()은 통신을 종료하기 전에 "데이터를 보내거나 받지 않겠다"고 알리는 함수이다. 옵션 설정에 따라 송신 버퍼(SD_SEND) 또는 수신 버퍼(SD_RECV)만 닫거나(하나만 막는 걸 Half-close 라고 부른다) 둘 다 닫을 수 있다(SD_BOTH).
MMORPG에선 "통신이 끊기면 너 잘못"이라 치부하고 넘기는 경우가 많기 때문에 게임 서버에선 shutdown()을 굳이 사용하지 않는다.
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET에 송신 버퍼를 닫는 코드이다.
::shutdown(serverSocket, SD_SEND); // 데이터를 보내지 않겠다고 알림
.
.
.
::closesocket(serverSocket); // 소켓 리소스 반환
SO_SNDBUF, SO_RCVBUF
SO_SNDBUF, SO_RCVBUF는 각각 송신 버퍼의 크기, 수신 버퍼의 크기를 의미한다. setsockopt()를 통해 버퍼 크기를 변경하거나, getsockopt()을 통해 크기를 읽어올 수 있다.
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET에 송/수신 버퍼의 크기를 출력하는 코드이다.
int32 sendBufferSize;
int32 optionLen = sizeof(sendBufferSize);
::getsockopt(serverSocket, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufferSize, &optionLen);
cout << "송신 버퍼 크기 : " << sendBufferSize << '\n';
int32 recvBufferSize;
optionLen = sizeof(recvBufferSize);
::getsockopt(serverSocket, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufferSize, &optionLen);
cout << "수신 버퍼 크기 : " << recvBufferSize << '\n';
// 실행 결과
// 송신 버퍼 크기 : 65536
// 수신 버퍼 크기 : 65536
SO_REUSERADDR
SO_REUSERADDR는 사용한 IP주소 및 port를 재사용하고 싶을 때 사용하는 옵션이다. 서버가 강제로 닫히는 경우에 사용 중이었던 IP주소 및 port가 일정시간이 지날 때까지 서버를 재시작하지 못할 수 있다. 이럴때 SO_REUSEADDR 옵션을 켜서 재사용을 허용하면 서버를 재시작하지 못하는 불편함을 해결할 수 있다.
설정해두면 개발 단계시 편하니 어지간하면 설정하는 것이 좋다.
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET에 주소 재사용을 활성화하는 코드이다.
{
bool enable = true;
::setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable));
}
TCP_NODELAY
TCP에는 Nagle 알고리즘이 적용되어 있다. (Nagle 알고리즘은 데이터가 충분히 크면 보내고, 그렇지 않으면 충분히 쌓일 때까지 대기하며 전송을 미루는 알고리즘이다.)
Nagle 알고리즘 사용 시 모아서 보내기 때문에 작은 패킷이 불필요하게 많이 생성되는 일을 막을 수 있다는 장점이 있지만, 반응성이 낮아진다는 단점이 있기 때문에 반응성이 매우 중요한 게임서버에서는 사용하지 않는다. (모아서 처리하고 싶은 경우 코드를 짜는 것이 더 편하기 때문인 것도 있다.)
아래 예제는 serverSocket이라는 이름의 생성해둔 SOCKET의 TCP에서 Nagle 알고리즘을 비활성화하는 코드이다.
{
bool enable = true;
::setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable));
}
깃헙 코드
https://github.com/pledge24/WinGameServerPractice/tree/b5fc2aeb07b408f8c973800b1603ea4f8cc37483
'C++ > Windows 게임서버' 카테고리의 다른 글
[C++] 게임서버 탐구 일지 #5. Winsock TCP 서버 - 논블로킹 (0) | 2024.12.28 |
---|---|
[C++] 게임서버 탐구 일지 #3. Winsock UDP echo 서버 (0) | 2024.12.27 |
[C++] 게임서버 탐구 일지 #2. Winsock TCP 서버 기초- send(), recv() (0) | 2024.12.27 |
[C++] 게임서버 탐구 일지 #0. TCP 통신 기초 이론 (1) | 2024.12.25 |
[C++] 게임서버 탐구 일지 #1. Winsock TCP 서버 기초- 클라이언트-서버 연결하기 (0) | 2024.12.25 |