서버/네트워크

4. 소켓 옵션

광란의슈가슈가룬 2024. 9. 1. 09:36

이번에는 간단하게 소켓 옵션을 설정하는 방법에 대해 알아보자.

 

소켓을 만들 때 기본적으로 커널에서 샌드, 리시브 버퍼(송수신 버퍼)가 만들어진다. 이 버퍼도 크기를 변경하는 등 프로그래머가 옵션을 설정할 수 있다. 

::setsockopt();
::getsockopt();

이 두 함수를 통해 옵션을 설정하고 가져올 수 있다. 자세한 내용은 마이크로소프트 공식문서에 잘 나와있다. 

https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-setsockopt

 

setsockopt 함수(winsock2.h) - Win32 apps

setsockopt 함수(winsock2.h)는 모든 형식의 소켓과 연결된 소켓 옵션의 현재 값을 모든 상태로 설정합니다.

learn.microsoft.com

간단하게 몇 가지만 알아보았는데 만약 필요한 것이 있다면 문서를 참고해서 사용하면 된다.

#include "pch.h"
#include "CorePch.h"
#include <Windows.h>
#include "ThreadManager.h"

#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
	// 관련 정보가 wsaData에 채워짐
	WSAData wsaData;
	if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		return 0;

	// UDP
	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 = 지연하다
	// send -> closesocket 할 경우 send 버퍼에 데이터가 남음
	// 송신 버퍼에 있는 데이터를 보낼 것인지 여부
	// onoff = 0이면 closesocket()이 바로 리턴, 0이 아니면 linger초 만큼 대기(default 0)
	// linger = 대기시간
	LINGER linger;
	linger.l_onoff = 1;
	linger.l_linger = 5;
	::setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));

	// 일방적으로 closesocket를 하는 것이 아니라
	// Half-Close, 즉 반만 닫음으로써 미리 알려줌
	// SD_SEND		: send 막음
	// SD_RECEIVE	: recv 막음
	// SD_BOTH		: 둘 다 막음
	//::shutdown(serverSocket, SD_SEND); // 더 이상 보낼 것이 없다!
	// 소켓 리소스 반환
	//::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));
	}

	// IPPROTO_TCP
	// TCP_NODELAY = Nagle 네이글 알고리즘 작동 여부
	// 데이터가 충분히 크면 보내고, 그렇지 않으면 데이터가 쌓일 때까지 대기
	// 장점 : 작은 패킷이 불필요하게 많이 생성되는 것 방지
	// 단점 : 반응 시간 손해
	// 일반적으로 게임서버의 경우 꺼줌(반응 시간이 더 중요!)
	{
		bool enable = true; // true일 때 끄는 것!!
		::setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable));
	}

	// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
	::WSACleanup();
}

자주 사용되는 몇 가지 함수만 알아보았다. 주석으로 간단하게 설명해 놓았으므로 후술하진 않겠다. 이 외에 다른 많은 함수들이 있으니 필요할 경우 공식 문서를 참조하도록 하자.

'서버 > 네트워크' 카테고리의 다른 글

6. Select 모델  (0) 2024.09.05
5. 논블로킹(Non-Blocking) 소켓  (2) 2024.09.03
3. UDP 서버  (2) 2024.08.30
2. TCP 서버  (0) 2024.08.29
1. 소켓 프로그래밍  (0) 2024.08.29