파일 설명자가 참조하는 소켓에 대한 옵션을 조작합니다.
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
성공 시 : 0 반환
실패 시 : -1 반환
Netfilter를 사용하면 관련 처리기와 함께 사용자 지정 소켓옵션 정의 할 수 있습니다.
이 때 반환 값은 처리기의 반환 값입니다.
옵션값 | 데이터형 | 설명 |
---|---|---|
SO_BROADCAST | BOOL | 브로드캐스트 메시지 전달이 가능하도록 한다. |
SO_DEBUG | BOOL | 디버깅 정보를 레코딩 한다. |
SO_DONTLINGER | BOOL | 소켓을 닫을때 보내지 않은 데이터를 보내기 위해서 블럭되지 않도록 한다. |
SO_DONTROUTE | BOOL | 라우팅 하지 않고 직접 인터페이스로 보낸다. |
SO_OOBINLINE | BOOL | OOB 데이터 전송을 설정할때, 일반 입력 큐에서 데이터를 읽을 수 있게 한다. 이 플래그를 켜면 recv(:12)나 send(:12)에서 MSG_OOB 플래그를 사용할 필요 없이 OOB 데이터를 읽을 수 있다. |
SO_GROUP_PRIORITY | int | 사용하지 않음 |
SO_KEEPALIVE | BOOL | Keepalives를 전달한다. |
SO_LINGER | struct LINGER | 소켓을 닫을 때 전송되지 않은 데이터의 처리 규칙 |
SO_RCVBUF | int | 데이터를 수신하기 위한 버퍼공간의 명시 |
SO_REUSEADDR | BOOL | 이미 사용된 주소를 재사용 (bind) 하도록 한다. |
SO_SNDBUF | int | 데이터 전송을 위한 버퍼공간 명시 |
소켓을 이용한 서버프로그램을 운용하다보면 강제종료, 비정상 종료 되는 경우가 발생합니다. 테스트 목적으로 할 경우, 특히 강제 종료 시켜야 하는 경우가 자주 발생하는데,
bind error : Address already in use
이러한 에러메시지를 종종 보게된다. 이는 비정상 종료된 상태로 커널이 bind정보를 유지하고 있는 상태이며 1-2분뒤에 커널이 알아서 정리하지만 테스트 할 때 기다리는 것은 번거롭습니다.
int sock = socket(...);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&bf, (int)sizeof(bf));
위와 같은 설정으로 (기존에 bind된 소켓 자원을 프로세스가 재 사용할 수 있도록 허락하는 코드) 이러한 부분을 해결 할 수 있습니다.
SO_SNDBUF는 전송 소켓에서 사용할 최대 버퍼의 크기를 설정합니다.
커널은 setsockopt로 설정한 값의 2배를 버퍼 크기로 잡습니다.
전송소켓 버퍼의 기본 크기는 /proc/sys/net/core/wmem_default에서,
설정 가능한 최대 크기는 /proc/sys/net/core/wmem_max에서 확인할 수 있습니다.
SO_RCVBUF는 수신 소켓에서 사용할 최대 버퍼 크기를 설정합니다.
마찬가지로 커널은 2배 크기로 잡습니다.
전송소켓 버퍼의 기본 크기는 /proc/sys/net/core/rmem_default에서, 설정 가능한 최대 크기는 /proc/sys/net/core/rmem_max에서 확인할 수 있습니다.
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **artv)
{
int s;
int bsize = 0;
int rn;
rn = sizeof(int);
s = socket(AF_INET, SOCK_STREAM, 0);
// 현재 전송 소켓 버퍼의 크기를 가져온다.
getsockopt(s, SOL_SOCKET, SO_SNDBUF, &bsize, (socklen_t *)&rn);
printf("Send buf size : %d\n", bsize);
// 현재 버퍼 크기에 * 2를한다.
bsize = bsize * 2;
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &bsize, (socklen_t)rn);
// 현재 버퍼크기를 가져온다.
// 커널은 * 2만큼 버퍼크기를 설정하므로, 최초 크기의 4배만큼 설정됐을 것이다.
getsockopt(s, SOL_SOCKET, SO_SNDBUF, &bsize, (socklen_t *)&rn);
printf("Send buf size : %d\n", bsize);
}
SO_LINGER는 소켓이 close되었을 때 소켓 버퍼에 남아있는 데이터를 어떻게 할 것인지 결정하기 위해 사용합니다.
SO_LINGER를 제외한 모든 소켓 수준 옵션은 optval이 정수를 가리키고 optlen이 정수 크기로 설정될 것으로 예상합니다.
struct linger
{
int l_onoff; // linger 옵션을 끌것인지 킬 것인지 결정
int l_linger; // 기다리는 시간의 결정
}
두 멤버 변수로 close 방식을 정할 수 있습니다.
l_onoff == 0 일 때 (우아한 연결 종료 보장)
l_linger의 영향을 받지 않으며, 소켓의 기본 설정으로 버퍼에 남아있는 모든 데이터를 보냅니다. 이때 close는 바로 리턴하며 작업은 백그라운드에서 이루어집니다.
l_onoff > 0 이고 l_linger > 0 일 때
버퍼에 남아있는 데이터를 모두 보냅니다(우아한 연결 종료). 이 때 close는 l_linger에 지정된 시간만큼 블럭상태에서 대기합니다.
만약 지정된 시간 내에 남아있는 데이터를 모두 보냈다면 리턴이 됩니다.
지정된 시간 내에 보내지 못했다면 에러와 함께 리턴됩니다.
l_onoff > 0 이고 l_linger == 0 일 때 (hard, abortive 종료)
close는 바로 리턴하며 소켓 버퍼에 남아있는 데이터는 버린다.
TCP 연결 상태일 경우 상대편 호스트에 리셋을 위핸 RST 패킷을 보냅니다.
hard, abortive 종료라고 부릅니다.
https://www.joinc.co.kr/w/Site/Network_Programing/AdvancedComm/SocketOption
https://www.ibm.com/docs/en/zos/2.1.0?topic=calls-setsockopt