Chapter 09 소켓의 다양한 옵션

시캉·2024년 7월 3일
1

ft_irc

목록 보기
9/13

09-1 소켓의 옵션과 입출력 버퍼 크기

소켓의 다양한 옵션

소켓이 지니는 다양한 속성들을 파악하고, 특성을 필요에 맞게 변경하여 사용하는 것은 중요하다.

소켓 옵션은 프로토콜 레벨(계층)에 따라 구분된다.

  • SOL_SOCKET : 소켓에 대한 가장 일반적인 옵션
  • IPPROTO_TCP : TCP 프로토콜에 대한 사항
  • IPPROTO_IP : IP 프로토콜에 대한 사항.

수많은 옵션들이 있으나 중요한 옵션 몇가지에 대해서만 알아보자.

getsocket & setsocket

#include <sys/socket.h>
// 소켓의 옵션 받아오기. 성공 시 0, 실패 시 -1
int getsocketopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

// 소켓의 옵션 세팅하기. 성공 시 0, 실패 시 -1
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
  • sock : 옵션확인을 위한 소켓의 파일 디스크립터 전달.
  • level : 확인할 옵션의 프로토콜 레벨 전달.
  • optname : 확인할 옵션의 이름 전달.
  • optval : 확인결과의 저장을 위한 버퍼 주소 값 전달.
  • optlen : 네 번째 매개 변수 optval 로 전달된 주소 값의 버퍼크기를 담고 있는 변수의 주소 값 전달. 함수 호출이 완료되면 이 변수에는 네 번째 인자를 통해 반환된 옵션정보의 크기가 바이트 단위로 계산되어 저장됨.

예를 들어, getsockopt 함수를 통해 SO_TYPE 옵션으로 소켓의 타입정보(TCP or UDP)를 받는다고 하면,

getsocketopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
getsocketopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);

와 같이 함수를 호출하여, sock_type 변수에 담기는 값을 확인하면 소켓의 타입을 확인할 수 있다.
-> TCP는 SOCK_STREAM의 상수값인 1, UDP는 SOCK_DGRAM의 상수값 2가 sock_type에 담김.

SO_SNDBUF & SO_RCVBUF

입출력 버퍼와 관련이 있는 소켓옵션이다.

#include <sys/socket.h>
getsocketopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
getsocketopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);

순서대로 출력버퍼, 입력버퍼의 크기를 알 수 있는 함수 호출이다.

int snd_buf = 1024*3;
int rcv_buf = 1024*3;

setsocketopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
setsocketopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&snd_buf, sizeof(snd_buf));

변경하고자하는 값을 optval 인자 자리에 담아 전송하면 해당 값으로 소켓 옵션이 변경된다.

한 가지 신기한 점은 위처럼 변경한 버퍼의 크기를 다시 getsocketopt를 통해 확인해보면, 1024*3(3kb) 가 아니라는 점이다.

입출력 버퍼는 주의깊게 다루어져야 하는 부분이기에, 우리 요구대로 버퍼 크기가 정확히 맞추어지지 않을 수 있다
다만 우리의 요구를 어느정도 반영하여 버퍼크기를 책정한다.

09-2 SO_REUSERADDR

해당 옵션을 이해하기 전, time-wait상태에 대해 먼저 알아보자.

time-wait

앞 챕터에서 Four-way handshaking 에 대해 공부했다.
서버와 클라이언트가 연결된 상태에서 종료되는 경우, FIN, ACK를 서로 두 번씩 총 네 번의 메시지를 주고받아 종료를 안정적으로 한다고 보았다.

하지만 서버가 예기치 못하게 종료되는 경우, Four-way handshaking을 진행할 수 없게 될 것이다.
따라서 이 과정을 온전히 완료할 수 있게끔 존재하는 것이 time-wait이며 서버가 ctrl + C 를 입력받더라도, 소켓 연결이 바로 종료되는 것이 아니라는 것이다.

서버와 클라이언트 간의 안전한 연결 종료를 위해 타임 웨잇을 필요한 부분이지만, 해당 서버가 종료되어 바로 재실행해야하는 경우 문제가 된다. 이미 할당된 포트이기 때문에 동일한 포트로 다시 바인드하려고 하면 바인드 에러가 발생하기 때문이다.

이 때 사용하게 되는 것이 SO_RESUERADDR 옵션이다. 기본적으로 소켓은 0(false) 값을 가지고 있는데, 이는 time-wait 상태에 있는 소켓의 포트 번호는 할당이 불가능함을 의미한다. 이 값을 setsocketopt를 통해 1로 변경해주면 time-wait 하고 있는 포트에 새로운 소켓 바인딩이 가능해진다.

09-3 TCP_NODELAY

Nagle 알고리즘

  • 앞서 전송한 데이터에 대한 ACK 메시지를 받아야만, 다음 데이터를 전송하는 알고리즘
  • ACK가 수신될 때 까지, 최대한 버퍼링하여 다음 데이터를 전송하게 된다. (패킷 수 절약)
    -> Nagle 알고리즘을 적용하지 않으면 네트워크 트래픽에는 않좋은 영향이 발생할 수 있다.

하지만, Nagle 알고리즘이 항상 좋은 것은 아니다. 용량이 큰 파일 데이터 전송을 한다면, 출력버퍼를 거의 꽉 채운 상태에서 패킷을 전송하므로, 패킷 수가 크게 늘지도 않고 ACK를 기다리지도 않다보니 전송속도도 훨씬 빠름.

Nagle 알고리즘 중단

Nagle 중단은, 알고리즘 적용 여부에 따라 트래픽 차이가 크지 않고 데이터 전송은 빠른 경우에 진행해야한다.

int optval = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&optval, sizeof(optval));

소켓 옵션 TCP_NODELAY를 1로 변경해주도록 하자.


마치며

9장에서는 소켓에 사용되는 여러가지 옵션들을 확인했다.
ft_irc에서도 setsocketopt를 통해 SO_REUSEABLE 옵션을 켰는데, 그 이유가 동일한 포트를 재사용할 수 있게끔 하기 위한 세팅이었다는 사실을 깨닫게 되었다.

참고 : https://www.brainkart.com/article/Socket-Options_9116/

2개의 댓글

comment-user-thumbnail
2024년 7월 3일

정리를 아주 잘 해놓으셨네요! 다음에 설명들으러 가겠습니다!!

1개의 답글

관련 채용 정보