상세 컨텐츠

본문 제목

소켓 옵션

똑똑한 개발/C++ 게임개발

by 성댕쓰 2021. 11. 8. 22:18

본문

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)

'똑똑한 개발 > C++ 게임개발' 카테고리의 다른 글

Select 모델  (0) 2021.11.11
논블록킹 소켓  (0) 2021.11.09
UDP 서버실습  (0) 2021.11.06
TCP vs UDP  (0) 2021.11.05
TCP서버 실습  (0) 2021.11.05

관련글 더보기

댓글 영역