ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TCP/IP 소켓 프로그래밍 - 9장 : 소켓의 다양한 옵션
    네트워크 2024. 6. 19. 15:56


    9장

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

    옵션의 참조(get) 및 변경(set)에는

    getsockopt 함수와 setsockopt함수를 사용한다.

     

    #include <sys/socket.h>
    
    int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
    //성공 시 0, 실패 시 -1 리턴
    
    //level : 확인할 옵션의 프로토콜 레벨 전달
    //optname : 확인할 옵션의 이름 전달
    //optval : 확인 결과 저장을 위한 버퍼 주소값 전달
    //optlen : 함수 호출이 완료되면 네번째 인자를 통해 반환된 옵션 정보 크기가 
    //         바이트 단위로 계산되어 저장됨.

     

     

    #include <sys/socket.h>
    
    int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
    //성공 시 0, 실패 시 -1 리턴

     

    아래 사진은 getsockopt 함수를 이용해 TCP와 UDP의 type 정보를 출력하는 코드를 실행한 결과이다.

     

    소켓 타입은 생성 시 한번 결정되면 변경 불가능하다.

     

    소켓 생성 시 입력 버퍼와 출력 버퍼가 생성된다.

     

    SO_RCVBUF는 입력 버퍼 크기와 관련된 옵션이고,

    SO_SNDBUF는 출력 버퍼 크기와 관련된 옵션이다.

    위 두 옵션을 이용하면 입출력 버퍼 크기 참조, 변경이 가능하다.

     

    아래 사진은 소켓 생성 시 기본적으로 생성되는 버퍼들의 크기를 출력하는 예제 코드 실행 결과다.

     

    아래 사진은 소켓의 입출력 버퍼를 임의 크기로 변경하기 위해 시도한 예제 코드 실행 결과다.

     

    주의할 점은 우리가 입력한 값을 어느 정도 반영을 하는 것이지 그대로 설정되지는 않는다는 점이다.

    (설정한 size는 1024*3이었다. )


    09-2 SO_REUSEADDR

    "주소 할당 에러 발생"

    예제에 나온 코드를 실행할 때,

    클라이언트가 먼저 연결 종료를 하는 경우 (FIN 메시지 송신)는 문제 없이 서버의 재실행이 가능하다.

     

    서버가 먼저 연결 종료를 하는 경우, 서버의 재실행은 동일 port 번호에 대해 bind error가 난다.

    위 두 차이는 누가 FIN 메시지를 먼저 전송했는지에 달려 있다.

    "Time-wait 상태"

    서버가 먼저 FIN 메시지를 보내면, Time-wait 상태에 들어가게 된다.

     

    Time-wait 상태는 먼저 FIN 메시지를 보낸 호스트만 거친다.

    이 때문에, 바로 이어서 서버의 재실행이 불가능한 것이다. (Port 번호가 사용중!!)

     

    “Time-wait 상태는 왜 존재하는가?”

     

    호스트 B가 마지막 ACK (seq : 1202, ack : 3002)를 못받았을 경우, 호스트 B는 재전송을 요청하게 된다.

    이 때, Time-wait 상태를 거치지 않으면, 호스트 A의 소켓이 소멸했으므로

    더이상 통신이 불가능하고 호스트 B는 영원히 마지막 ACK 메시지를 받지 못하게 되는 것이다.

     

    서버를 즉시 재실행하고 싶지만 time-wait 상태가 길어져 문제가 생길 경우,

    SO_REUSEADDR의 상태를 변경하면 된다.

     

    Time-wait 상태에 있는 소켓에 할당되어 있는 port 번호를 새로 시작하는 소켓에 할당되게 할 수 있다.

     

    SO_REUSEADDR의 디폴트 값은 0(False)이고,

    이는 time-wait 상태에 있는 소켓의 port번호는 할당 불가능함을 의미한다.

    이 값을 1(True)로 바꿔주면, 서버가 먼저 종료메시지를 날리고 Time-out 상태가 되어도,

    즉시 재실행이 가능해진다.

     

    optlen = sizeof(option);
    option = TRUE;
    setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen);

     

    아래 사진은

    서버를 강제 종료하고 즉시 재실행할 때 문제 없이 같은 포트 번호로 돌아가게 하는 코드 실행 결과다.


    09-3 TCP_NODELAY

    “Nagle 알고리즘”

    Nagle 알고리즘은 네트워크상의 패킷 수를 줄이기 위해 고안되었다.

    Nagle 알고리즘은 앞서 전송한 데이터에 대해 ACK 메시지를 받으면

    다음 데이터를 전송하는 알고리즘이다.

     

    Nagle 미적용시에는 ACK 메시지와 상관없이 데이터를 송신하게 되는 것이다.

    TCP는 ACK이 수신될 때까지 최대한 버퍼링을 해서 데이터를 전송한다. ( TCP는 기본적으로 Nagle 적용 )

     

    Nagle을 적용해 Hello 문자열을 전송했을 때는 총 4개의 패킷이 송수신 되었고,

    Nagle 미적용시 Hello 문자열을 전송했을 때는 총 10개의 패킷이 송수신 되었다.

     

    네트워크 트래픽을 줄이기 위해서 Nagle 알고리즘을 적용해야 한다.

     

    “Nagle 알고리즘이 항상 좋은 것은 아니다. “

     

    ‘용량이 큰 파일 데이터 전송’처럼

    1. nagle 알고리즘 적용 여부에 따른 트래픽 차이가 적고
    2. 데이터 전송이 적용했을 때보다 빠른 경우가 있다.

    파일 데이터를 출력버퍼로 밀어넣는 작업은 시간이 별로 걸리지 않아,

    nagle을 적용하지 않아도 출력버퍼를 거의 꽉 채운 상태에서 패킷을 전송하게 된다.

    거기에 ACK을 기다리지 않아 전송속도도 향상된다.

     

    정리하면,

    nagle 알고리즘 미적용시 속도의 향상을 기대할 수 있다.

    그러나 네트워크 트래픽에 부담을 주어 좋지 않은 결과가 나올 수도 있다.

     

     

    nagle 알고리즘을 중단하는 방법은 아래와 같다.

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

     

    nagle 알고리즘 설정상태를 확인하는 방법은 다음과 같다.

    int opt_val;
    socklen_t opt_len;
    opt_len = sizeof(opt_val);
    getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, &opt_len);



Designed by Tistory.