본문 바로가기
연구소👨‍💻/CS연구소

[네트워크] TCP, UDP 프로토콜

by 신그자체김상범 2024. 6. 19.

들어가며

지난번에 IP 관련 공부를 했다. 그럼 당연히 이제 전송 계층 공부를 할 차례 잠깐 들어가기전에 지난번 IP 패킷의 정보를 담고있는 IP헤더의 구조와 크기를 정리해봤다

전송 계층 공부

전송계층을 공부하게되면 누구나 TCP, UDP라는걸 배우게된다. 일단 둘은 판이하게 다르고 실제로 응용프로그램 계층의 프로토콜들도 UDP냐 TCP냐에 따라서 목적과 용도가 다르다는걸 알 수 있었다. 사실상 우리가 인터넷 계층에서 사용하는 프로토콜은 IP가 대부분이라는걸 생각하면 ( 실제로 UDP와 TCP도 둘다 IP프로토콜을 사용한다고한다) 둘의 차이점이나 구성을 명확히 파악해야 할 것 같다.

일단 책에서 배운것을 기반으로 둘의 차이를 생각해보면

TCP

  1. 연결성이다
  2. 데이터들의 도착순서가 보장된다.
  3. 가상회선 방식을 사용한다
  4. 위의 이유로 신뢰성이 확보된다
  5. 연결방식으로 핸드세이크 방식을 사용한다.

UDP

  1. 비연결성이다
  2. 데이터들의 도착순서가 보장되지 않는다.
  3. 데이터 유실을 보장하지 않는다
  4. 오버헤드가 적어 전송 속도가 빠르다

이건 아주 명확한 구분이다. 이제 어떤 조건에서 어떤 전송 계층 프로토콜을 사용할 것이냐는 질문을 받으면 신뢰성과 연결성, 데이터 전송속도를 기준으로 판단하면 될 것 같다.

그런데 진짜 중요한건 얘네들이 왜 이렇게 구현되는지다 특히 이런 질문들을 하게 됐다.

  1. UDP가 핸드셰이크 없이 데이터를 전송하면 받는 쪽에서 어떻게 그걸 인식하고 저장할까
  2. 가상회선 방식은 책들에서 찾아보면 라우터 경로(홉)를 저장해놓는 것으로 보이는데 그걸 어떻게 구현 할까?

서버 포트는 항상 열려있다.

서버는 특정 포트를 리스닝 하는 기능을 가지고있다. 그리고 데이터그램 방식으로 데이터를 전송하는 UDP는 목적지의 ip와 포트값을 담고 있다. 이떄 정해진 포트로 UDP정보가 오게되면 서버는 그냥 그 데이터를 받는다. 물론 이는 UDP 포트만의 특성이 아니다 TCP역시 데이터를 받기위해선 서버가 포트를 리스닝상태로 만들어야한다. 그런데 이제 TCP는 요청이 들어오면 먼저 핸드셰이크를 해야한다. 그 과정없이 들어오는 데이터들은 그냥 폐기처분된다. 반면 UDP는 비연결성이므로 특정 데이터가 들어오면 그걸 반드시 받는다.

요약하면 서버는 통신에 대한 포트를 항상 열어놓아야 하기 때문에 핸드셰이크가 없어도 데이터를 받을 수 있다.

가상회선은 논리적 구조다

맨처음에 난 가상회선이 모든 라우터 경로를 TCP 헤더안에 붙이지 않을까? 라고 생각했다. 왜냐면 IP주소에는 도착지의 IP에 대한 정보만 존재하지 그 경로는 없기 때문이다. 그래서 TCP헤더의 구조를 살펴봤다.

그런데 여기서 보다시피 TCP헤더에도 목적지로 가는 중간 경로 홉들의 IP에 대한정보들은 존재하지 않았다. 목적지 포트에 대한 정복만 있을 뿐이었다.

그래서 그럼 각 IP패킷은 특정 가상회선 식별자를 가지고 각 라우터에서 그 식별값을 가지고 다음으로 보내줄 ip주소를 저장해놓는건가 하는 생각이 들었다. 하지만 관련 내용을 마땅한 설명이 없었다.

그런데 한 위키에서 가상회선 방식은 물리적 구조가 아닌 논리적 구조라는 것을 알게됐다. 요약하자면 TCP라고 해서 항상 패킷들이 같은 홉들을 경유해서 목적지에 도착하는게 아닐 수도 있다는 소리다.

논리적 구조와 동적 라우팅

여기서 IP 동적 라우팅이라는 개념을 알게 됐는데 우리가 특정 목적지를 잘 담은 데이터를 경유 라우터에 갖다 놓게되면. 그곳에서는 목적지까지의 네트워크 속도, 목적지까지의 라우터 수를 변수 삼아 그중 최적의 경로로 데이터를 보낸다는 것이다.

그럼 가상회선 방식은? 이라고 생각했는데 가상회선 방식은 종단간 어플리케이션이 서로 연결되었음을 나타내는 것이지 항상 모든 경로가 일치한 상태에서 데이터를 전송하는 것이 아니라고한다. 그럼에도 TCP는 데이터 전송 순서나 신뢰성을 위한 다양한 장치가 있기 떄문에 종단만 연결되어있어도 온전히 데이터가 전달되고 그것을 회선이라고 하는 것 같다.

마찬가지로 UDP도 동적 라우팅 방식을 쓴다고한다. 애초에 UDP와 TCP는 전송계층 프로토콜이고 라우터를 결정짓는 프로토콜은 인터넷계층의 프로토콜이어야한다는 것도 이때 깨달았다.

TCP 방식은 어떻게 데이터를 유지하나?

만약 가상회선 연결이 논리적인것이고 물리적으로 일어나지 않는다면 TCP도 UDP가 그랬던 것처럼 데이터의 순서가 뒤죽박죽 들어올 수 있다는 것이된다. 그러면 TCP는 이걸 방지하기 위해서 어떤 방식을 사용할지가 추가로 궁금해졌다.

핸드셰이크와 데이터 전송

핸드셰이크를 알려면 일단 SYN과 ACK에 대해서 알 필요가 있다.

SYN(synchronize) : 여기서는 목적지에게 연결해달라는 요청으로 쓰인다

ACK(acknowledgement) : 목적지로의 연결이 허용되었을 경우 상대편은 ACK에 특정한 값을 담아 돌려보낸다.

아까의 TCP 헤더의 구조를 잘 보면 4바이트짜리 Sequence number, Acknowledge Number와 ACK, SYN이란 단어가 써져있는 칸들을 확인할 수 있다.

여기서 ACK와 SYN은 지금 ACK 방식, SYN방식이 사용된다는 플래그다. 만약 이 값이 1이 아닌경우엔 Sequence number과 Acknowledgemet number의 값은 무효가된다.

처음 핸드셰이크의 경우엔 SYN플래그가 켜지고 Synchronize number에 난수가 담긴다 그러면 서버는 이걸 읽고 ACK용으로 난수 1에서 1을 더한값을 저장하고 새로운 난수를 SYN에 설정한다. 플래그는 SYN과 ACK둘다 켜놓는다.

근데 이방식은 데이터를 전송할 떄도 반복된다. 데이터를 보내게 되면 SYN플래그는 꺼지지만 Sequence Number에는 패킷의 데이터 순서를 나타내는 정보가 담기게된다. 그리고 서버는 받은 값의 숫자를 보고 패킷의 순서를 알 수 있게 되는 것이다.

또한 클라이언트가 연결을 종료하기 위해 4-way handshake를 할때 사용하는 FIN 플래그 역시 존재한다. FIN을 할때 역시 Sequence number에 숫자를 두는데 이 수는 마지막으로 전송한 데이터의 순서값과 같다 그리고 서버는 FIN을 받은것을 ACK에 1을 더하고 전송한다

여기까지 왔으면 왜 TCP에서 연결은 3way-handshake이면서 해제는 4way-handshake인지 알 수 있게된다.

3way 핸드셰이크와 4way 핸드셰이크 차이

애초에 handshake는 대칭적으로 일어나고있다 처음 연결할 떄는 SYN과 ACK가 각각 한번씩 마지막엔 FIN과 ACK가 각각 1번씩 이건 같은 TCP연결에서 클라이언트가 서버에 데이터를 보낼 수도 있고 데이터를 받을 수도 있기 때문이다. 그리고 tcp헤더를 살펴보면 FIN플래그, ACK플래그, SYN플래그가 동시에 존재하기 때문에 한 패킷은 SYN 플래그와 ACK 플래그를 동시에 표현할 수 있다.

그런데 서버와 처음 연결할 때 클라이언트에서 SYN을 보내면 서버는 굳이 ACK와 SYN을 다른 패킷으로 보낼 이유가 없다. 그래서 그 과정이 1번으로 합쳐져서 일어나는 것이다.

반면 연결을 종료하는 일은 서버에서 클라이언트에게 데이터를 보내는 중에 일어날 수 있다. 그러니 또 굳이 패킷을 합쳐서 보내지 않는 것이다. 이런 데이터를 보내고 받는 과정을 생각하면 TCP연결에 대해서 보다 직관적으로 이해할 수 있게 된다. 또한 이렇게 TCP의 신뢰성을 생각하면 FIN연락보다 특정 패킷이 늦게 도착할 수 있고 이것도 가상회선 방식이 한개의 경로로만 오지 않기 때문에 그렇다. 그러다보니 TCP 클라이언트는 FIN을 받고 ACK를 보낸 뒤에도 TIME_WAIT상태를 가지게된다.

이렇게 TCP의 연결 방식에 대해서 살펴보았다.

TCP헤더는 이렇게 발신지 포트 주소(2바이트), 목적지 주소 (2 바이트), SequenceNumber(4바이트) , Acknowledgement(4바이트), SYN, ACK, FIN에 대한 플래그 (3비트) 외에도 약 7바이트 5비트 분량의 내용들이 남아있다. (옵션 제외) 하나씩 짧게 설명해보면.

HLEN : Tcp헤더의 길이 4비트로 표시

예약 : 추후 확장성을 위해 마련해놓는 자리, 사용하지 않는다. 6비트 표시

CWR (Congestion Window Reduced): CWR 플래그는 혼잡 제어(congestion control)의 일환으로 사용, TCP 플로우의 송신자는 혼잡 윈도우 크기를 줄였음을 상대에게 알리기 위해 이 플래그를 설정

ECE (ECN-Echo): ECE 플래그는 Explicit Congestion Notification (ECN)을 지원하는 네트워크에서 사용 이 플래그는 네트워크 장비가 혼잡 상태를 감지했음을 표시함.

URG (Urgent): URG 플래그는 긴급 데이터를 포함하고 있음을 나타냅니다. TCP 헤더에 URG 플래그가 설정되어 있으면, 긴급 데이터가 포함된 TCP 세그먼트임을 표시.

PSH (Push): PSH 플래그는 수신측 TCP 스택에게 데이터를 즉시 처리하도록 요청, PSH 플래그가 설정된 TCP 세그먼트는 버퍼링을 최소화하고 데이터를 가능한 빠르게 상위 계층으로 전달함.

RST (Reset): RST 플래그는 TCP 연결을 강제로 종료하고 초기화하는 데 사용, RST는 예기치 않은 상황이나 오류 상황에서 TCP 연결을 복구하려고 할 때 사용된다. 일반적으로 RST는 잘못된 시퀀스 넘버로 인한 연결 문제를 해결하기도함.

Checksum : Ip 헤더에 존재하는 것처럼 데이터의 무결성을 체크해주는 16비트의 칸

urgent pointer : URG 플래그가 켜졌을 때 작동할 것 같으며 아마 패킷 요소중 긴급한 부분을 지시하는 칸일 것 같다.

Window size : 데이터를 받는쪽이 지금 남는 버퍼사이즈가 얼마나 되는지 알려주는 16비트의 칸이다. 수신자가 알리는 것이기 때문에 ACK에 설정되어서 보내진다. 흐름제어를 위해서 사용하고 클라이언트가 이걸 바탕으로 보낼 수 있는 데이터양을 조절하는 것 같다.

아직 내가 모르는 요소중에 혼잡제어, 긴급요소를 관리하는 부분이 있는 것같다. 하지만 아직 이부분은 다루지 않고 마지막으로 UDP는 총 8바이트의 작은 헤더를 갖는다. 내용도 훨씬 단순해서 TCP의 대부분의 요소들을 제거한 것처럼 생겼다.

'연구소👨‍💻 > CS연구소' 카테고리의 다른 글

[C++] 예외 처리  (0) 2024.06.26
[C++] 다형성과 오버로딩, 오버라이딩  (0) 2024.06.22
[네트워크] IP프로토콜 분석  (1) 2024.06.16
[OS] 멀티프로세스, 멀티스레딩  (1) 2024.06.13
[C++] 람다 함수  (0) 2024.06.12