TCP Socket통신할 때 다양한 Option을 줄 수 있다. 알아보자.
::setsockopt(...)로 socket option을 설정하고, ::getsockopt(...)로 socket option을 쿼리할 수 있다.
이 둘 함수의 첫 번째 파라미터는 연결한 socket이다.
두 번째 level은 옵션을 해석하고 처리할 주체를 의미한다.
SOL_SOCKET | 소켓 코드 |
IPPROTO_IP | IPv4 |
IPPROTO_TCP | TCP 프로토콜 |
SO_KEEPALIVE로 연결상태를 주기적으로 확인할 지 설정할 수 있다. 옵션을 사용하면 상대편 연결이 끊어지는 것을 감지할 수 있다.
SO_LINGER는 소켓 통신 닫을 때 지연할 지 선택하는 옵션이다.
::shutdown함수를 이용해 Half-Close 기능을 구현할 수 있다. Half-Close는 소켓을 닫을 때 send 파트만 닫고 receive는 열어놓는 개념이다. 상대 socket이 전송하려는 데이터를 모두 받거나 이 쪽 소켓이 닫힌다는 것을 알리고 종료하도록 유도할 수 있다.
SO_SEND | send 막기. |
SO_RECEIVE | recv 막기. |
SO_BOTH | 둘 다 막기. |
SO_SNDBUF, SO_RCVBUF option을 이용하여 송신 버퍼 크기, 수신 버퍼 크기를 설정 및 조회 할 수 있다. 설정의 경우 반드시 설정 값으로 바뀌는게 아니다. 너무 큰 값(ex 1GB)으로 세팅하면 OS가 무시할 수도 있다.
SO_REUSEADDR는 IP 주소 및 port를 재사용하는 option이다. 연결이 비정상 종료되어 깔끔히 제거되지 않으면 사용하던 IP와 port를 다른 연결에서 사용 못하는 경우가 있다. 해당 옵션을 켜두면 사용한 IP와 port를 재사용할 수 있다.
IPPROTO_TCP level에 TCP_NODELAY는 Nagle 알고리즘 작동 할 지 설정하는 옵션이다.
Nagle 알고리즘을 사용하면 데이터가 충분히 크면 보내고, 그렇지 않으면 데이터가 충분히 쌓일 때까지 대기한다.
따라서 Nagle 알고리즘 사용할 때 장 단점은 다음과 같다.
장점 | 작은 패킷이 불필요하게 많이 생성되는 일을 방지 |
단점 | 반응 시간 손해 |
게임에서는 반응 시간이 적은것을 우선하기에 Nagle 알고리즘을 사용하지 않는다. 따라서 TCP_NODELAY 켜두는 것을 선호한다.
GameServer.cpp
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
void HandleError(const char* cause)
{
int32 errCode = ::WSAGetLastError();
cout << cause << " ErrorCode : " << errCode << endl;
}
int main()
{
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData))
return 0;
SOCKET serverSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket == INVALID_SOCKET)
{
HandleError("Socket");
return 0;
}
// level : 옵션을 해석하고 처리할 주체
// 소켓 코드 -> SOL_SOCKET
// IPv4 -> IPPROTO_IP
// TCP 프로토콜 -> IPPROTO_TCP
// SO_KEEPALIVE = 주기적으로 연결 상태 확인 여부 (TCP Only)
// 상대방이 소리소문 없이 연결 끊는다면?
// 주기적으로 TCP 프로토콜 연결 상태 확인 -> 끊어진 연결 감지
bool enable = true;
::setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&enable, sizeof(enable));
// SO_LINGER = 지연하다
// 송신 버퍼에 있는 데이터를 보낼 것인가? 날릴 것인가?
// onoff = 0이면 closesocket()이 바로 리턴. 아니면 linger초만큼 대기 (default 0)
// linger : 대기 시간
LINGER linger;
linger.l_onoff = 1;
linger.l_linger = 5;
::setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
// Half-Close
// SD_SEND : send 막는다
// SD_RECEIVE : recv 막는다
// SD_BOTH : 둘다 막는다
//::shutdown(serverSocket, SD_SEND);
// 소켓 리소스 반환
// send -> closesocket
//::closesocket(serverSocket);
// SO_SNDBUF = 송신 버퍼 크기
// SO_RCVBUF = 수신 버퍼 크기
int32 sendBufferSize;
int32 optionLen = sizeof(sendBufferSize);
::getsockopt(serverSocket, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufferSize, &optionLen);
cout << "송신 버퍼 크기 : " << sendBufferSize << endl;
int32 recvBufferSize;
optionLen = sizeof(recvBufferSize);
::getsockopt(serverSocket, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufferSize, &optionLen);
cout << "수신 버퍼 크기 : " << recvBufferSize << endl;
// SO_REUSEADDR
// IP주소 및 port 재사용
{
bool enable = true;
::setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable));
}
// IPPROT TCP
// TCP_NODELAY = Nagle 네이글 알고리즘 작동 여부
// 데이터가 충분히 크면 보내고, 그렇지 않으면 데이터가 충분히 쌓일때까지 대기!
// 장점 : 작은 패킷이 불필요하게 많이 생성되는 일을 방지
// 단점 : 반응 시간 손해
{
bool enable = true;
::setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable));
}
// 윈속 종료
::WSACleanup();
}
참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)
댓글 영역