-
TCP 소켓 옵션Computer Science/Network 2023. 1. 28. 05:47
흔히 사용되는 소켓 옵션을 정리한다.
SO_KEEPALIVE
이 옵션을 활성화하면 상대방에게 약 2시간 간격으로 TCP 패킷을 보낸다.
이 패킷에 대해 상대가 ACK 패킷으로 응답하면 응용 프로그램에 통보 없이 커널 선에서 살아 있음 확인하고 마무리한다.
아무런 응답이 없거나 RST 패킷을 받으면 자동으로 소켓을 닫는다.
TCP 연결에서는 데이터 교환이 없는 건지, 상대 시스템이 다운되거나 네트워크 연결이 불가능한지 알 수 없다.
소켓 생성이 늘어나면 시스템 자원 소모도 비례해서 늘어나므로 연결이 끊어진 소켓을 닫아줄 필요가 있다.
이때 SO_KEEPALIVE를 설정하여 주기적으로 불필요하게 열린 소켓을 닫을 수 있다.
하지만 커널에서 주기가 보통 2시간으로 되어 있기 때문에 비정상적인 종료를 빠른 시간내에 감지하는 데에는 적절치 않다.
이런 경우에는 응용 프로그램에서 heart-beat을 구현하거나 다른 방법을 사용해야한다.
SO_KEEPALIVE는 좀비 소켓을 정리하는 정도의 용도로만 사용하도록 하자.
SO_LINGER
이 옵션은 소켓을 close 했을 때 동작 방식을 제어할 수 있게 해준다. // 정상종료와 비정상종룔 제어
SO_LINGER를 활용하면 연결을 닫기 전, 보내지 않은 데이터가 전송될 때까지 지정된 시간동안 기다릴 수도 있고 즉시 데이터를 버릴 수도 있다.
SO_LINGER의 설정 값에는 두 가지 멤버 변수가 있다.
l_onoff : Linger 옵션에 대한 활성화, 비활성화 플래그
l_linger : Linger 옵션이 되었을 때 기다리는 시간
위 두 멤버 변수 값에 따라
1. l_onoff == 0
Linger 비활성화, 소켓의 default 값이다. 소켓 버퍼에 남아있는 모든 데이터를 전송하는 일반적인 정상 종료가 이루어짐.
2. l_onoff > 0 && l_linger == 0
close가 즉시 리턴 되고 소켓 버퍼에 남아있는 데이터를 버리는 비정상 종료가 이루어짐. 상대방에게는 RST 패킷을 보냄.
3. l_onoff > 0 && l_linger >0
지정한 시간동안 대기하고 버퍼에 남아있는 데이터를 모두 보냄.
지정된 시간내에 모두 보냈다면 정상적으로 리턴되고 시간이 초과되면 에러와 함께 리턴된다.
SO_REUSEADDR
이 옵션을 사용하면 여러 프로세스가 이미 사용중인 IP 주소와 포트 번호로 bind()를 성공적으로 호출할 수 있고, 커널이 소켓을 사용하는 중에도 계속해서 사용할 수 있다.
서버 프로그램이 종료된 후에도 커널이 소켓의 포트를 점유 중인 경우에 서버 프로그램을 다시 구동해야할 때 유용하다.
예를 들어 close 단계에서 상대방으로부터 FIN 패킷을 받고 FIN_ACK 패킷을 전송한 후 일정 시간동안 소켓을 종료하지 않고 커널이 해당 소켓을 점유하게 되는데, 프로세스가 종료 되어도 커널에서 일정 시간동안 점유 중인 상태로 있게 된다.
이때 SO_REUSEADDR 설정이 없다면 응용 프로그램을 재실행하면 bind 함수를 호출할 때 아직 점유 중인 포트를 연결하려는 시도 때문에 에러가 발생한다. 일정 시간이 지나야 bind 가능하다.
SO_SNDBUF, SO_RCVBUF
이 옵션을 설정하면 커널의 수신 버퍼와 송신 버퍼의 크기를 조정할 수 있다.
목적지로 전송하는 데이터는 송신 버퍼에 저장되어 있다가 목적지 수신 버퍼로 전송되고, 수신 버퍼는 전송 받은 데이터를 응용 프로그램이 읽어 갈 때까지 보관하고 있는다.
수신 버퍼의 크기가 작으면 크기를 초과해서 전송 받은 데이터는 수신측에 버려진다.
물론 TCP 에서는 flow control 을 통해 윈도우 크기를 결정하기 때문에 수신 버퍼의 크기를 초과할 가능성은 적다.
그러나 UDP 에서는 flow control이 되지 않으므로 수신 버퍼의 크기를 초과할 가능성이 있다.
보통 연결 준비 단계에서 수신 버퍼와 송신 버퍼의 크기를 기준으로 윈도우 크기를 결정하므로(?)
(윈도우 크기는 매번 전송마다 갱신 되는 것으로 알고 있었는데 확인 필요)
SO_RCVBUF와 SO_SNDBUF의 값은 클라이언트에서는 connect 함수를 호출하기 전에 서버에서는 listen 함수를 호출 하기전에 해야 한다.
송수신 버퍼의 크기를 조정할 때는 주의 깊게 사용해야 한다.
커널이 사용자가 설정한 크기를 그대로 적용하지 않고, 설정한 값을 참조해서 커널의 정책에 따라 버퍼의 크기를 결정하기 때문에 사용자가 기대한 대로 적용되지 않을 수 있다.
SO_TIMEOUT
이 옵션은 소켓의 모든 I/O 메서드 타이머를 설정하여 영원히 기다릴 필요가 없도록 해준다.
서버 소켓에서 accept(), 모든 소켓에서 read() 또는 write()와 같은 작업에 적용된다.
SO_TIMEOUT이 없다면 read()의 경우 충분한 바이트를 읽을 때까지 블록되지만
SO_TIMEOUT를 설정하면 지정된 시간이 초과할 때 SocketTimeoutException, InterruptedIOException와 같은 예외가 발생한다.
Java 소켓은 Write Timeout을 제공하지 않아서 Java 소켓 통신의 Socket Timeout은 Read Timeout 만을 의미한다.
Connection Timeout은 서버의 장애 상황으로 connection 조차 맺어지지 못할 때 발생하는 timeout 이고,
(Socket) Read Timeout은 connection은 맺어졌지만 I/O 작업이 길어지거나 락이 걸려 요청이 처리되지 못 하고 있을 때 발생하는 timeout이다. 클라이언트가 더 이상 기다리지 못 하고 커넥션을 끊는다.
TCP_NODELAY
이 옵션을 활성화하면 Nagle 알고리즘을 비활성화 한다.
네트워크에서 Nagle 알고리즘은 "가능하면 조금씩 여러 번 한번에 많이 보내라"를 원칙으로 만들어진 알고리즘이다.
Nagle 알고리즘을 사용하면 데이터를 보내고 ACK 패킷을 받을 때까지 출력 버퍼에 데이터를 저장하였다가
ACK 패킷을 받으면 버퍼에 저장되어 있는 데이터를 보낸다.
네트워크 전송량보다 빠른 응답속도를 중요시 할 때 이 옵션을 사용한다.
참고
https://springsource.tistory.com/141
https://velog.io/@jyongk/TCP-%EC%86%8C%EC%BC%93-%EC%98%B5%EC%85%98-SOLINGER
'Computer Science > Network' 카테고리의 다른 글
TCP Keepalive 와 HTTP Keepalive (0) 2024.02.10 TCP의 혼잡제어 (0) 2020.10.06 TCP의 흐름제어, 오류제어 (0) 2020.10.03 로드밸런서(Load Balancer)란?? (0) 2020.09.30