[열혈TCP/IP] 09 소켓의 다양한 옵션

2 minute read

09 소켓의 다양한 옵션

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

  • 소켓의 옵션은 계층별로 분류된다. 책 200페이지

getsockopt & setsockopt

#include <sys/socket.h>
getsockpot()
#include <sys/socket.h>
setsockpot()
  • 소켓 타입은 소켓생성시 한번 결정되면 변경이 불가능하다

SO_SNDBUF & SO_RCVBUF

//소켓 입출력버퍼의 크기 확인 및 수정

09-2 SO_REUSEADDR

주소할당 에러 발생(Binding Error)

클라에서 강제로 종료를 진행하면 Four-way handshaking을 거치는데 이 때 FIN을 전송한다. 클라 쪽에서 콘솔을 닫든, ctrl + c를 입력하든 모두 FIN이 전송된다.

서버쪽에서 ctrl+c와 같은 방법으로 FIN이 호출되면, 서버를 종료하고 나서 서버의 재실행에 문제가 생긴다. 동일한 port번호를 기준으로 ㅅ ㅓ버를 재실행하면 bind() error라는 메시지가 출력되고 서버는 실행되지 않는다. 하지만 이상태에서 약 3분정도 기다리면 정상적인 실행을 다시 할 수 있다.

Time-Wait 상태

Four-way handshaking의 과정은 아래와 같았다.

  1. A: 연결 끊고싶습니다. FIN SEQ 5000, ACK -
  2. B: 잠시만요 ACK SEQ 7500, ACK 5001
  3. B: 저도 끊을 준비가 됐습니다. 끊으세요. FIN SEQ 7501, ACK 5001
  4. A: 네 연결 끊겠습니다. ACK SEQ 5001, ACK 7502

ACK SEQ 5001, ACK 7502를 받은 호스트 B는 소켓을 소멸시킨다. 그런데 ACK SEQ 5001, ACK 7502를 전송한 호스트 A는 Time-wait의 시간을 가지고 잠시 뒤에 소켓이 소멸된다. 소켓이 time-wait의 상태인 경우에는 소켓의 PORT 번호가 사용중이기 때문에 앞서 말했던 “서버를 재시작하는 경우”에는 소켓의 포트가 사용되고 있기 때문에 bind를 할 수 없었던 것이다.

time-wair의 시간을 가지는 것은 클라/서버 상관없이 먼저 연결 종료를 요청한 쪽이다. 하지만 클라의 time-wait는 신경쓰지 않아도 되는데, 그 이유는 클라의 port는 임의로 할당되기 때문이다. 즉, 서버와 달리 프로그램이 실행될 때마다 port번호가 유동적으로 할당되기 때문에 time-wait 상태에 대해 신경쓰지 않아도 된다.

위 예시에서 호스트 B는 ACK SEQ 5001, ACK 7502를 받아야 소켓을 소멸시키는데 A가 ACK SEQ 5001, ACK 7502를 보내고 먼저 소켓을 소멸시키면 ACK SEQ 5001, ACK 7502 메시지가 중간에 사라질 수 있고 이렇게되면 호스트 B의 소켓은 소멸하기 위한 신호를 받지 못한다. 그래서 조금 시간차를 두고 소멸시킨다.

위 Four-way handshaking의 메시지를 1/2/3/4라고 할 때 A가 B에게 보낸 4가 소실되면, B는 A가 3을 받지 못한 것으로 생각하고 3을 다시 보낸다. 그런데 A는 3을 받으면 4를 보내면서 time-wait에 들어가는데 네트워크 상태가 좋지 못하면 이 과정이 계속 반복할 수 있다.

이를 해결하기 위해서는 아래와 같이 SO_REUSEARRD 옵션을 바꾸면 된다.

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

SO_REUSEADDR의 default 값은 0(false)로, 이는 time-wait 상태에 있는 소켓의 PORT 번호는 할당이 불가능함을 의미한다. 이 값을 1(true)로 바꾸면 time-wait된 port번호를 재할당하는 것이 가능하다.

09-3 TCP_NODELAY

Nagle 알고리즘

이 알고리즘은 네트워크 상에서 돌아다니느 패킷들이 흘러 넘침을 막기 위한 알고리즘이다. TCP 상에서 적용되는 매우 단순한 알고리즘으로써, 이이 적용여부에 따른 데이터 송수신의 차이는 아래와 같다.

  • Nagle 알고리즘 OFF
    1. ‘n’,’a’,’g’,’l’,’e’ 이 각각 data로 전송됨
    2. 각각에 대한 ACK이 수신됨
    3. 총 패킷 10개 송수신
  • Nagle 알고리즘 OF
    1. ‘n’ 전송
    2. ACK ‘n’
    3. ‘agle’ 전송
    4. ACK ‘agle’
    5. 총 패킷 4개 송수신

나글 알고리즘은 앞서 전송한 데이터에 대한 ACK 메시지를 받아야만 다음 데이터를 전송하는 알고리즘이다.

기본적으로 TCP 소켓은 나글알고리즘을 적용해서 데이터를 송수신한다. 때문에 ACK가 수신될 때까지 최대한 버퍼링을 해서 데이터를 전송한다. “Nagle”을 전송할 때 데이터를 출력버퍼로 이동시키다. 이때 출력버퍼가 비어있기 때문이 ‘n’가 들어오면 바로 전송을 하다. 그리고 나머지 데이터를 출력버퍼에 쌓아두고 ACK가 수신되면 출력버퍼에 있는 데이터를 전송한다.

만약 용량이 큰 파일 데이터의 전송을 할 때에는 Nagle 알고리즘을 적용하지 않는 것이 더 좋을 수 있다. 파일 데이터를 출력버퍼로 밀어 넣는 작업은 시간이 걸리지 않는다. 때문에 나글 알고리즘을 적용하지 않아도 출력버퍼를 거의 꽉 채운 상태에서 패킷을 전송하게 된다. 따라서 패킷의 수가 크게 증가하지도 않을뿐더러, ACK를 기다리지 않고 데이터를 전송하니 전송속도도 놀랍게 향상된다.

즉, 나글 알고리즘을 적용하지 않으면 속도의 향상을 기대할 수 있으나, 무조건 적용하지 않을 경우 트래픽에 상당한 부담을 주게 되어 더 좋지 않은 결과를 얻을 수 있다.