[네트워크] TCP Retransmission

3way handshake

Posted by Start Bootstrap on July 25, 2021 · 3 mins read

TCP 서버를 구현할 일이 있어서 구현을 하다보니 7계층 말고는 기억도 안나는 (이마저도 기억도 가물가물) 네트워크를 공부하게 되었다.

여차저차 해서 네티로 TCP 서버를 구현하였는데.. 연결이 끊어지는 현상이 발생했다.. (서버 특성상 연결은 계속 유지되어 있어야 하는 상황)

KeepAlive 타임이 끝나기도 전에.. 아래와 같은 타이머가 도는 상황이 발생

    
netstat -c --timer | grep "192.0.0.1:36483             192.0.68.1:43881"

tcp        0      0 192.0.0.1:36483             192.0.68.1:43881            ESTABLISHED keepalive (5.39/0/2)
tcp        0      0 192.0.0.1:36483             192.0.68.1:43881            ESTABLISHED keepalive (4.14/0/2)
tcp        0    210 192.0.0.1:36483             192.0.68.1:43881            ESTABLISHED on (0.21/2/2)
tcp        0    210 192.0.0.1:36483             192.0.68.1:43881            ESTABLISHED on (0.68/3/2)
    

ESTABLISHED 상태면 연결은 된 상태인데..

상황을 이해하기 위해 TCP의 3-way handshake 부터 알아보자

3-way Handshake

클라이언트가 서버로 데이터를 전송할때 서버가 데이터를 받을 준비가 안 된 상황에서 데이터를 일방적으로 전송하면 서버가 데이터를 정상적으로 처리할 수 없어 데이터가 버려지기 때문에 데이터를 안전하게 보내고 받을 수 있는지 미리 확인하는 작업을 거치는데 TCP에서는 3번 패킷을 주고받으면서 통신을 서로 준비하므로 3way-handshake 라고 한다.

SYN 내말들리니?

SYN + ACK 응, 너도 내말 잘들리니?

ACK 응...!!!

이후로부터는 연결이 이루어지고 (ESTABLISHED) 데이터 전송이 이루어진다.

Retransmission 은 왜 발생하지??

데이터 패킷 분실

TCP 통신에서 데이터를 보내고 난 뒤 커널에 정의된 Retransmission Timer 를 가동하게 되는데 Timer 시간 만료 전까지 ACK를 못받으면 데이터를 재전송 한다.

위의 경우는 매우 단순한 경우이지만, 결과적으로는 설정된 타이머 내에서 ACK를 못받으면 Retransmission Timer가 동작한다.

그러면 무한정 도는걸까?

ss -i 확인해보자

해당 커넥션의 RTO/RTT 값을 확인할 수 있다.

RTO, Retransmission Timeout, 재전송 타임아웃

RTT, 패킷망(인터넷) 상에서 상대측 호스트까지 패킷이 왕복하는데 걸리는 시간

위 커넥션의 종단 거리는 평균 0.972ms 이고 204ms 동안 값에 대한 응답이 없으면 다시 전송하는걸 알 수 있다.

여기서 204ms 가 지나면 다음 타이머는 2배인 408ms 까지 값을 기다리고 다음은 그 2배를 기다린다.

그러면 앞선 물음인 무한정 2배씩 늘어나는걸까?

sysctl -a | grep -i retries 실행해보자

net.ipv4.tcp_retries1 , net.ipv4.tcp_retries2 TCP는 기본적으로 재전송하기 위한 임계치 값으로 두개의 값을 가지고 있다. 두 값 모두 최종적으로는 재전송 횟수를 정의하지만, 첫번째 값은 IP 레이어에 네트워크가 잘못되었는지 확인하도록 사인을 보내는 기준이 되며, 두번째 값은 더이상 통신을 할 수 없다고 판단하는 기준이 된다. 간단하게 첫번째 값을 soft threshold, 두번째 값은 hard threshold라고 보면 된다. 결과적으로는 두번째 값에 정의된 횟수만큼을 넘겨야 실제 연결이 끊어진다.

요약

TCP 재전송은 신뢰성 있는 통신을 위해 내가 보낸 패킷을 상대방이 받았다는 응답을 받지 못하면 패킷이 유실되었다고 판단하고 다시 보내는 로직이며, TCP의 특성상 자주 발생하지는 않아도 반드시 발생할 수 밖에 없는 현상이다. 애플리케이션의 적당한 환경 설정을 통해서 불필요한 타임아웃 메시지를 막고 서비스의 품질을 높일 수 있다.

사실 결론은 요약처럼 정리할 수 있긴한데...

결국 중요한 문제는 클라이언트에서 특정 커맨드를 받고 리스폰스를 못받으면 통신을 끊고 다시 연결하는 로직이 숨어 있었다.

내가 짠 코드를 너무 신뢰하지말자.. 가장 먼저 의심해야하는 부분일듯??