란디의 메모장

TCP, UDP의 역사

2018. 8. 15. 07:21

■ IP. 모든 것의 허리.


TCP/IP 라는 프로토콜 체계는 나중에 두 개로 분리된 것이 아니라, 인터넷 프로토콜 표준이 제정될 때부터 지금처럼 두 개로 분리되어 제안되었다. David Clark 교수님의 88년 논문은 TCP/IP 를 설계할 때 어떤 우선순위로 설계되었는지에 대해서 설명하고 있는데, 그중에서 “여러 종류의 데이터 전달 방식을 지원할 것 (multiple types of delivery services)” 이라는 목적을 달성하기 위해서 가장 기본이 되는 기능만을 IP 에 포함시키고, 그 위에 TCP 라는 새로운 계층을 얹는 형태가 된 것이다.





▲ TCP/IP 모델은 IP 가 허리인 모래시계 형태로 표현된다.


이 그림에서 주의 깊게 바라볼 부분은 잘록한 허리 부분인 IP 다. TCP/IP 의 의도된 디자인인데, 어떤 통신 관련 하드웨어 기술이든 IP 만 구현하면 되고, 어떤 응용 프로그램이든 IP 위에서만 동작하게 하면 된다는 실로 엄청난 의미를 내포한다.

유선 랜 (Ethernet 이라 불림)과 WiFi 모두 IP 주소를 쓰고, 우리가 잘 인식하지는 못하지만, Bluetooth, LTE 나 3G 도 통신을 할 때는 IP 주소를 부여받아 통신한다. 그 덕에 까페의 WiFi 에서나 길을 걸으면서 LTE 상에서도 똑같은 앱을 이용해 스마트폰에서 음악을 들을 수 있다. 즉 어떤 통신 방법이든 IP 를 구현하는 한, IP 위에서 동작하던 응용 프로그램을 지원하는 데 문제가 없으며, 어떤 응용 프로그램이든 IP 로 통신한다는 것을 전제만 하면 IP 를 지원하는 어떤 하드웨어 기술에서도 동작할 수 있다. 추가적으로 IP 만 지원하면 어떤 기술이든 된다는 개념은 각각 기술을 분리시켜서 빠른 개발을 할 수 있는 여건을 만들어 주었다.


...


IP 가 서로 다른 네트워크를 연결한다고 했으니 각각의 네트워크는 독립된 관리자가 존재할 것이다. 적어도 카이스트 네트워크 관리자가 포항공대 관리자와 같을 거라고는 생각되지 않는 것처럼 말이다. 그리고 각 네트워크가 독립적으로 동작한다는 것은 여러 네트워크를 거쳐야 하는 IP 입장에서는 ‘반드시 통신 가능하다’ 라는 것을 보장해줄 수 없다는 것을 뜻한다. 카이스트 네트워크를 거쳐가야 되는데 카이스트가 연휴기간 동안 시스템 점검을 할 수도 있는 것 아닌가? 그래서 열심히 통신해보려고는 하는데 반드시 된다는 보장은 못 한다는 의미로, IP를 설명하는 중요한 특성으로 best effort 라는 표현을 쓴다.


■ TCP, 불안정성 위에 구축한 안정성


이처럼 기간(基幹) 프로토콜이 통신의 안정성을 보장해주지 못한다면 그걸 이용해서 뭔가를 한다는 것은 굉장히 괴로운 일이 된다. ...

TCP는 안정적으로 패킷이 가지 못하는 경우 이걸 캐치해서 안정적으로 갈 수 있게 전송을 제어하는 역할을 하는 프로토콜이라는 뜻이다. 그런데 어떻게 불안정한 것 위에 안정성이라는 것을 보장해주는 것이 가능할까? 그 비결은 ACK 와 타임아웃, 그리고 재전송이다.

ACK 는 받은 걸 받았다고 알려주는 것을 의미한다. Acknowledgment 라는 영어 단어의 앞 세 글자를 딴 것인데 영어 단어에서 의미하듯, “잘 받았습니다.”라는 뜻이다. TCP 데이터가 물줄기처럼 흐른다고 해서 stream이라는 표현을 쓰는데, 이것도 복잡하니까 그냥 다들 그러는 것처럼 패킷이라고 하자. TCP는 패킷을 받을 때마다 ACK 이라는 별도의 패킷을 만들어서 “잘 받았습니다, 제가 다음에 받아야 하는 것은 몇 번 패킷입니다.”를 보낸 쪽에 알려준다. 그리고 보낸 쪽에서는 이걸 이용해서 잘 가고 있는지를 판단한다.



1) 클라이언트는 서버에 접속을 요청하는 SYN(a) 패킷을 보낸다.
2) 서버는 클라이언트의 요청인 SYN(a)을 받고 클라이언트에게 요청을 수락한다는 ACK(a+1) SYN(b)이 설정된 패킷을 발송한다.

3) 클라이언트는 서버의 수락 응답인 ACK(a+1) SYN(b) 패킷을 받고 ACK(b+1)를 서버로 보내면 연결이 성립(establish)된다.


ACK 이 오지 않는 경우는 두 가지를 생각해볼 수 있다. 보낸 패킷이 받는 쪽에 도착을 못해서 받는 쪽이 ACK 을 보내야 하는 것을 아예 모르는 경우와 받는 쪽은 패킷을 받았고 ACK 도 보냈는데 ACK 이 불안정한 IP 때문에 중간에 배달 사고를 만난 경우이다. 전자는 패킷을 다시 보내는 것이 말이 되는데 후자는 ACK 만 보내면 될 것 같다. 하지만 서로 통신을 못하는 상황에서는 이 둘을 효율적으로 구분하면서까지 문제를 해결하지는 못한다. 그 때문에 어떤 경우든 일정 시간 동안 ACK 을 못 받으면 패킷을 다시 보내버린다. 그게 약간 더 비효율적일 수 있지만, 상당히 더 확실하고 속 편한 방법이다.

정리하자면, TCP는 패킷을 보낼 때 다음 그림과 같은 일을 겪는다. 1) 받은 패킷에 대해서 잘 받았다는 뜻으로 ACK 을 보내준다. 2) ACK 을 일정 시간 동안 못 받으면 원래 패킷을 재전송한다.





■ TCP ACK 의 두 가지 방법: cumulative ACK, selective ACK


물론 TCP 가 이렇게 패킷을 하나씩만 전송하지는 않는다. Sliding window이라는 개념을 이용해서 보낼 수 있는 만큼을 연속으로 계속 보낸다. (우리가 쓰는 MS Windows가 아니라, 창문을 조금 열고 많이 열고 하는 것처럼 양을 조절한다는 뜻에서 window이다) 하지만 이처럼 ACK 을 안 받고 연속으로 쏘는 것은 물량에 제한이 있다. 그리고 ACK 을 못 받는 순간 결국 더는 아무것도 안 보내고 기다리는 상황이 발생한다. 재전송이 결정될 때까지 말이다.

그런데 한 번에 하나씩 보내고 받고 하는 것이 아니라 여러 개를 연속으로 보낸다면, 가운데 패킷이 유실된 경우는 어떻게 될까? 매우 좋은 질문이다. TCP의 기본 동작은 뭔가 유실되면 그 뒤에 아무리 정상적으로 와도 다 버리고 유실된 것부터 재전송하는 것이다.(예를 들어 1,2,3,4,5,6,7,8,9,10을 보냈는데 1,2,X,4,5,6,7,8,9,10 이렇게 됬다면 45678같은 뒤에 보내다가 다시 3부터 다시 4567... 보내야 하는 것)  이건 상당히 비효율적으로 보인다. 왜 유실 된 것만 다시 보내지 않고 이렇게 할까?

전자는 ACK 의 의미가 “앞의 것은 다 받았고 이걸 못 받았어. 여기부터 다 다시 보내줘” 라는 뜻이 되는 것이고, 후자는 ACK 의 의미가 “이것은 받았어. 앞에거 뒤에 거를 받았는지는 따로 알려줄께.” 가 된다. 그래서 전자를 “앞의 것은 다 받았다” 라는 뜻으로 누적적이라고 해서 cumulative ACK 이라고 하고 후자를 받았는지 여부를 선택적으로 알려준다고 해서 selective ACK 이라고 한다.


Cumulative ACK 은 한 번에 주르륵 여러 개 패킷을 보냈다고 하더라도 문제가 되는 ACK 하나만 기억하면 된다. 반면 selective ACK 은 여러개 패킷에 대해서 각각 ACK 을 받았는지를 모두 기억해야 된다. 다시 말해 cumulative ACK 은 기억해야 되는 것도 적고, 구현도 간단한 반면 selective ACK 은 기억해야되는 것도 많고 구현도 복잡해지는 것을 의미한다. 이는 효율성과 복잡성에 대한 전형적인 트레이드오프 예라고 할 수 있겠다.


초기의 TCP 는 cumulative ACK 을 기반으로 했다. 그런데 뒤에 것을 다 버리다보니 비효율적이고, 이런 비효율성은 기간 프로토콜인 IP 가 패킷을 많이 까먹을수록 급속도로 비효율적으로 변하기 시작했다. 예를 들어 무선 통신에서의 IP 라면 까먹는 것도 많을테니 TCP 는 상당히 많은 양을 재전송 해야 된다. 그리고 사용자 입장에서 이건 매우매우매우매우 느리다고 느끼게 된다.통신망이 10% 불안정해지면, 속도가 10% 를 까 먹는 것이 아니라 몇 배 이상 속도가 안 나게 된다. 그 때문에 나중에는 selective ACK 을 TCP의 옵션으로 채택했다. 


■ TCP 교통 체증과 서비스 붕괴 사건


이렇게 ACK 을 보내는 방식으로 TCP는 불안정한 IP 위에서 안정성을 보장해줄 수 있었다. 그런데 모든 것이 해결된 것처럼 생각했는데, 1986년 10월 일대 사건이 일어난다. 당시 최첨단 32 kbps 회선이 40 bps 밖에 전송을 못 하는 일이 발생한 것이다. 10진수에서 k는 10^3을 뜻하니 40 k -> 30이니까. 연결이 무려 1,000분의 1의 속도밖에 못 내는 일이 발생한 것이다.

연구자들이 확인해보니 중간에 패킷을 전달하는 장비인 라우터가 문제였다. 그런데 그 장비가 고장이라는 뜻이 아니라 인터넷이 점차 여러 연구 기관에서 쓰다 보니 그 라우터가 과부하가 걸려서 상당히 많은 패킷을 놓치고 있던 것이다. 그런데 TCP는 패킷이 유실되는 확률이 올라가면 cumulative ACK 과 재전송 때문에 그 속도가 드라마틱하게 느려진다고 앞에서 설명했다. 그리고 모두 재전송을 시도하면 상황이 더 악화한다. 더 많은 패킷이 들어오니 라우터는 더 부하가 걸리고 더 많은 패킷을 놓친다. 그렇게 인터넷은 종말을 맞는 듯했으나, 이때 인터넷을 구한 히어로 반 제이콥슨 (Van Jacobson) 이 등장한다.


그는 패킷이 유실되면 무식하게 바로 쏴대지 말고, 이게 중간에 장비가 과부하 걸린 것일 수도 있으니 양을 조절하자고 제안한다. 양을 얼마로 제한하느냐면 일단 1개만 보내는 걸로…. 앞에서 여러 패킷을 주르륵 보낼 수 있다고 한 것을 기억하는가? 이전까지는 유실되면 유실된 것부터 그 주르륵의 남은 개수만큼을 보냈었다. 이걸 1개로 제한하는 획기적인 아이디어였던 것이다. 그리고 그 방법은 기가 막히게 적중했다. (물론 1개 보낸 패킷이 성공적으로 도달하면 빠른 속도로 동시에 보내는 패킷 개수를 회복해 나간다.)

이처럼 중간의 장비가 과부하 걸려서 패킷들을 놓치는 것을 길에 차들이 몰려서 꽉 막히는 것에 빗대서 체증 (congestion) 이라고 한다. (장비 체증이 아니라 네트워크 체증 (network congestion) 이라고 부른다.) 그리고 라우터 장비 등은 교통 교차로에 비유된다. 이 사건 이후로 TCP에는 이런 교통 체증을 벗어나기 위한 반 제이콥슨의 알고리즘이 도입되었다. TCP Tahoe의 등장이다. 

...

어쨌거나 반 제이콥슨은 1986년의 써드 임팩트 수준의 인터넷 붕괴를 막아냈다. 그리고 이제부터 패킷이 유실되면 뜨거운 것에 닿았을 때 손을 움츠리는 것처럼 TCP는 보내는 쪽에서는 얼른 양을 줄여서 “조심조심” 보내게 되었다.


■ 단순한 껍데기인 UDP


앞에서 IP는 온 힘을 다해서 보내기는 하지만 보장은 못 한다고 했다. 그 때문에 IP의 패킷 전달 서비스는 불안정하다고 했다. 갈 때도 있고 안 갈 때도 있다. TCP 는 이런 불안정한 것 위에 안정적인 패킷 전달을 구현하였다. 대신 세상에 공짜가 없듯이, TCP 의 안정성은 ACK 라는 부가적인 패킷과 타임아웃, 재전송이라는 비용을 지불하고 만들어졌다. TCP 는 자신이 이런 복잡한 절차를 모두 마치고 패킷들이 모두 안정적으로 수신된 다음에야 비로소 프로그램에 패킷을 건네준다.

그 때문에 TCP 는 느리다. 패킷 유실이 없다면 마찬가지 아닐까 생각할 수도 있지만, TCP 는 네트워크 체증을 막기 위해서 한 번에 보낼 수 있는 패킷 개수가 정해져 있다고 했다. 그래서 처음에는 적은 숫자를 보내고 유실이 없으면 이 숫자를 늘려나간다. 그 때문에 TCP 는 유실이 없다고 하더라도 한 번에 보낼 수 있는 패킷 갯수 제한 때문에 느리다. (그리고 그것 말고도 바로 보내지 않고 Nagle 알고리즘이라는 버퍼링 기법 등 구현상의 이유 때문에도 느리다.)

그럼 이렇게 무거운 TCP 를 쓰고 싶지 않으면 어떻게 해야 되나? 앞에서 인터넷을 설계할 때 다양한 서비스들을 돌릴 수 있도록 설계되었다고 했다. 만일 내 서비스가 안정성 대신에 속도가 중요하다고 하면 어떻게 해야 될까? 가장 흔하게 드는 예가 인터넷 전화라고 불리는 VoIP 다. 인간의 언어는 대충 단어 몇 개 못 알아들어도 전체 문장을 이해하는 데 문제가 없는 경우가 많다. (이를 Context Sensitive Language 라는 표현을 쓴다.) 조금 화가 날 수는 있다.

오히려 상대가 한 말이 늦게 전달되거나 단어 사이 간격이 들쭉날쭉하면 더 알아듣기 힘들다. 그러다가 딥빡이 오게 되고, 그러다가 애인과 싸우게 된다. 그 때문에 VoIP 는 인류의 평화와 연인들의 행복을 위해 패킷 재전송은 필요 없이 가능한 한 빠르게 전달하는 것을 목표로 한다. 그럼 이런 경우는 어떻게 하나? UDP 가 그런 역할을 한다.

사실 UDP 는 하는 일이 없는 더미라고 생각하면 된다. “물은 셀프입니다" 도 아닌데, “TCP 를 안 쓸 거면 그냥 더 낮은 수준의 IP 를 그대로 쓰세요.” 라고 하기에 좀 뭣하니까 TCP 랑 구색을 맞추기 위해서 껍데기로 UDP 가 만들어졌다. UDP 가 단순 껍데기 역할이기 때문에 그 특성은 IP 자체의 특성과 마찬가지다. 불 안정적인 best effort 에, 그냥 바로 쏘고 만다.

IP 패킷이 어떤 방식으로 경로를 선택하는지는 내 IGC 발표 슬라이드를 참고해주기 바란다. 어쨌거나, IP 는 최적의 경로를 따라 가는 것이 아니다. 그 때문에 IP의 꼭두각시인 UDP 역시 최적의 경로로 가지는 않는다. 보통 게임을 만들 때 “속도 때문에 UDP 를 쓴다.” 라고 하는데 이는 UDP 가 최적의 경로를 따라 간다는 뜻이 아니라 TCP 에 비해서 하는 일도 없고, 주변 신경도 안쓰기 때문에 상대적으로 더 빠르다는 뜻이다. TCP 와 달리 안정성 확보를 위해서 해야되는 일도 없고, 네트워크 체증 생각도 안하고 마구잡이로 보낼 수 있다.


■ UDP vs. TCP


TCP 는 네트워크 체증에 한 번 크게 데였기 때문에 조심조심하는데, UDP 는 이걸 신경 쓰지 않고 마구잡이로 보낸다고 했다. 그렇다면 이 둘이 동시에 존재하면 어떻게 될까? 아주 재미있게도, TCP 는 네트워크 체증이 생긴 줄 알고 전송 속도를 줄이고, 그 줄인 속도를 아싸 조쿠나 하면서 UDP 가 잡아먹는다. 이는 TCP 가 100 개 있고 UDP 가 1개 있어도 마찬가지가 된다. TCP 는 모두 빌빌대게 되는데 UDP 혼자 살아남는다.

... TCP 의 편리함과 UDP 의 속도를 결합해보자고 시도되는 것이 Reliable UDP 라는 것이다. 그런데 나는 Reliable UDP 를 다소 이단으로 생각하고 그게 만능이라고 신봉되는 것이 다소 사이비 같은 느낌을 강하게 갖는다. 그 이유는 TCP 와 UDP 가 각각 안정성과 속도라는 상충되는 목표를 가지고 만들어졌는데, 그 둘의 단점을 제외하고 장점만을 취한다는 것은 불가능하기 때문이다. 그건 마치 몸은 순발력 있게 보통 키에 슬림하면서 200kg 역기를 들어올리는 근력을 갖는 것과 같다. TCP 와 UDP 가 이미 안정성과 속도라는 트레이드 오프의 결과인 만큼 트레이드 오프를 무시하는 먼치킨은 존재할 수 없다.

예를 들어, 만일 UDP 를 이용해 TCP 수준의 안정성을 구현한다면 결국 그건 TCP 내부에서 처리하는 전송 알고리즘을 다 구현하는 것을 의미하고 결국 TCP 만큼 느려진다.


TCP, 그리고 UDP 쉽게 알아보는 두 개념과 차이점

[TCP] 3-way-handshake & 4-way-handshake


공유하기

facebook twitter kakaoTalk kakaostory naver band