TCP Half-Closing, Half-Closed Connection
최근 Docker 버그를 수정하면서 TCP 계층에 새로 알게 된 사실이 있습니다.
h.resp는 일반적인 TCPConn (tcp 소켓)인데, CloseWrite란 대체 뭘까..?
여태 TCP 프로그래밍으로 간단한 채팅 서버, 파일 전송, 웹 서버를 만들어보면서 전혀 본 적 없는 기능입니다.
연결을 닫으면 여타 프로그래밍 언어나 소켓 라이브러리에서는 Close를 사용할 텐데...?
해당 코드의 구현부를 찾아봅니다.
주석을 보니 TCP connection에서 Write Stream만 선별적으로 닫는 기능을 제공합니다.
신기하게 CloseRead도 존재하네요.
TCP는 Full Duplex 통신이라 Read Stream과 Write Stream이 따로 존재한다는건 이해가 됐습니다.
그런데 과연 이 CloseWrite는 대체 뭘 한다는걸까요?
closeWrite() 즉 외부로 제공된 CloseWrite()의 내부 코어 파트 부분이라고 추측할 수 있는데,
file discriptor의 posix 구현체에서 shutdown을 통해 특정 명령어를 전송하고 있습니다.
shutdown 함수의 내부 구현은
그냥 Syscall이었습니다.
리눅스는 fd로 모든 입출력을 다루니 그렇다 치는데,
의외인 점은 윈도우도 비슷하게 shutdown 명령을 제공한다는 점 입니다.
개인적으로 이게 굉장히 신기하다고 생각했습니다. posix와 매우 흡사한 Windows API라니??
여튼 본론으로 넘어가서, 이 Half-Close에 대해서 설명 해보겠습니다.
기본적으로 TCP는 양방향 통신이 가능합니다. 하지만 Half-Closed Connection 상태에서는 단방향 통신으로 변합니다.
이 Half-Closed Connection을 설명하기 위해서는 일반적인 TCP의 연결 종료 절차를 봐야 합니다.
TCP의 연결 종료 절차는 연결 절차(three-way handshake)와 다르게 4가지 절차로 이뤄져 있습니다.
흔히 연결 종료 절차를 4-way handshake라고 부르더라구요. RFC에 공식적으로 적혀있는 용어는 아니였습니다.
FIN을 전송하는 것은 상대방에게 "난 더 이상 보낼 데이터가 없다" 고 알리는 행위입니다.
따라서 FIN을 서로 주고받는 것이 연결 종료를 의미하는 것 입니다.
하지만 이 FIN을 두 Peer가 주고받는게 아니라 한쪽만 보내면 어떻게 될까요?
다시 말해서 4-way handshake의 4가지 절차에서 절반만 수행하면 어떻게 되는 걸까요??
바로 FIN을 보낸 Peer는 데이터를 전송할 수 없지만, 다른 Peer는 데이터를 보낼 수 있는 상태가 됩니다!
즉 양방향 데이터 전송이 가능한 Full-Duplex TCP 연결이,
단방향으로만 데이터 전송이 되는 Half-Duplex TCP 연결이 되는 것 입니다.
이러면 명시적으로 데이터의 방향성을 지정할 수 있으니 특정한 소켓 프로그래밍 상황에서 굉장히 유용할거라는 생각이 들었습니다. 왜냐면 단방향으로만 데이터를 보내야 하는 경우에, Flag변수 등을 이용해 Full-Duplex Connection을 논리적으로 Half-Duplex Connection으로 사용하는게 아니라,
Half-Close를 통해 Half-Duplex Connection으로 전환하면 더 개발 소요가 줄어들 것이기 때문입니다.
하지만 Half-Closed Connection에도 단점은 존재합니다.
그 단점은 바로 Half-Closed Connection의 불안정성입니다.
Half-Closed Connection 상태에서는 FIN을 보낸 피어의 TCP State가 FIN-WAIT-2 상태로 돌입합니다.
TCP가 네트워크 통신인 만큼, 어떤 상황에서 상대방과의 연결이 끊킬지 모릅니다. 근데 FIN-WAIT-2 에서 상대방과 연락 두절되면, 서버는 연결되지 않는 상대방을 기다리느라 운영체제의 Connection 자원을 소모하게 됩니다.
마치 Syn-Flooding 처럼 의도적인 DOS 공격도 가능하겠네요.
실제로도 FIN-WAIT-2는 유명한 문제고, 이 때문에 대부분의 unix 운영체제는 FIN-WAIT-2 상태에 대한 타임아웃을 설정할 수 있습니다.
다만 이런 TIMEOUT 임의 설정은 TCP의 공식 규약에 위반되는 행위입니다.
하지만 이런 특정 문제를 해결하기 위해 서버나 네트워크 장비, 보안 장비에서 이런 설정을 제공합니다.
원래대로라면 2*MSL(Maximun Segment Lifetime) 만큼 FIN-WAIT-2 상태를 유지해야 하지만, 운영체제 설정이나, 피어 사이에 있는 네트워크 장비, 보안 장비등의 네트워크 설정으로 인해 2*MSL이라는 시간이 보장되는건 아닙니다.
따라서 Half-Closed Connection은 앵간해서는 사용하지 않는게 좋고, 사용 하더라도 FIN-WAIT-2 상태를 연장시켜주기 위해 매우 짧은 간격의 Health Check가 구현되어야 할 것 같네요.
TCP Half-Close: a cool feature that is now broken - Excentis
TCP Half-Close: a cool feature that is now broken - Excentis
When we released a new version of the ByteBlower traffic generator a few years ago we started getting user reports saying the TCP traffic flows mysteriously “stopped” after a short time. Some users told us the traffic stopped after 10 to 30 second
www.excentis.com
위 아티클을 보고서 게시글을 작성하기로 결정했습니다.
Half-Close가 RFC 상에는 존재하지만 RFC가 완벽하게 지켜질 수 없기 때문에 사용에 주의해야 하는 점이 인상깊었습니다.
글 읽어주셔서 감사합니다. 글 내용에 오류가 있다면 꼭 알려주시길 바랍니다 !!