Network

[Network] L4 핵심 (TCP & UDP)

mxruhxn 2024. 11. 13. 01:41
728x90
반응형

TCP와 UDP 개요

  • TCP에만 연결(Connection, Session) 개념이 있다
  • 연결은 결과적으로 순서번호로 구현된다
  • 연결은 '상태(전이)' 개념을 동반한다

TCP 통신의 경우, Client/Server 구조를 기본으로 따른다.

 

<클라이언트>

  • 클라이언트의 어떤 프로세스가 Socket을 'open'하면 OS가 해당 소켓에 'TCP Port' 번호를 부여한다.
    • 남는 번호 아무거나 랜덤하게 할당된다.

<서버>

  • 서버는 Socket을 열어두고 '연결 대기(listen)' 상태이다.

이러한 TCP 통신을 하기 위해서는 통신 대상(Server)의 IP 주소와 접속 가능한 포트 번호를 알아야 '연결'을 시도해볼 수 있다.

 

만약 통신 대상 서버가 프로세스도 없고, 소켓이 열려있지 않아서 연결 대기 상태도 아닌데 클라이언트가 연결을 시도한다면, 서버의 TCP 계층에서 자동으로 연결할 수 없다는 응답을 처리한다.

TCP 연결 과정(3-way handshaking)

  1. 클라이언트와 서버는 Sequence Number를 각각 랜덤으로 생성한다
  2. 클라이언트는 SYN(클라이언트 시퀀스 넘버)를 서버에게 보낸다. - SYN_SENT
  3. 서버는 SYN 프레임을 받고, SYN(서버 시퀀스 넘버) + ACK(클라이언트 시퀀스 넘버 + 1)을 클라이언트에게 보낸다. - SYN_RCVD
  4. 클라이언트는 SYN + ACK 프레임을 받고, ACK(서버 시퀀스 넘버 + 1)을 서버에게 보낸다. - ESTABLISHED
  5. 서버는 ACK 프레임을 받는다 - ESTABLISHED

SYN, ACK와 같은 것들은 모두 Segment인데, 단순 내부 연결 관리 목적의 Segment이기에 구조가 다르다. 'IP 헤더 + TCP 헤더'로만 구성되며 Payload가 존재하지 않는다.

 

여기서 중요한 것은 클라이언트의 ESTABLISHED와 서버의 ESTABLISHED는 동시에 이루어지지 않으며 시간차가 존재한다는 것이다. 때문에, ESTABLISHED는 실제로 물리적인 연결을 의미하는 것이 아니라 클라이언트와 서버가 개념적으로 연결되었다고 '판단'하는 것이다. 다시 강조한다. TCP 연결은 물리적 연결이 아닌, 개념적 연결이다. 즉, TCP 연결은 실제 연결을 의미한다기 보다는 다음의 행위에 목적을 둔다. 즉, TCP 연결의 실체는 다음과 같다.

  • Seq 번호 교환 (중요)
  • 정책 교환
    • 이때 교환되는 데이터에는 여러가지가 있지만, 가장 중요한 것은 'MSS'가 몇인지를 알려주는 것이다
    • 클라이언트와 서버 각각의 MSS는 같을 수도 있지만 다를 수도 있다. 이에 대한 정보를 TCP 연결 과정을 통해 교환하여 조정해주는 것이다.

TCP 연결 종료(4-way handshaking)

연결이 있다면, 연결 종료 절차도 있을 것이다. 여기서 사용되는 것은 4-way handshaking이다.


특수한 경우가 아닌 이상, 연결을 시도하는 주체와 연결을 종료하는 능동적인 주체는 모두 '클라이언트'이다.

클라이언트는 Active CLOSE 과정을 거치고, 서버는 Passive CLOSE 과정을 거치는 것이 일반적이다.

 

  1. 클라이언트는 FIN + ACK를 서버에게 보내고, FIN을 기다린다. - FIN_WAIT1
  2. 서버는 FIN + ACK를 받고, ACK를 클라이언트에게 보낸다 - CLOSE_WAIT
  3. 클라이언트는 ACK를 받고, 계속해서 FIN을 기다린다. - FIN_WAIT2
  4. 서버는 FIN + ACK를 클라이언트에게 보낸다. - LAST_ACK
  5. 클라이언트는 FIN + ACK를 받고, 서버에게 ACK를 보내어 연경 종료를 기다린다 - TIME_WAIT
    • TIME_WAIT가 발생한 쪽이 연결 종료를 요청한 주체이다. 서버 측에서 TIME_WAIT가 발생했다면 연결 종료를 요청한 주체는 서버였을 것이다
    • TIME_WAIT이후 일정 시간이 지나면 자동으로 CLOSED 상태가 되고, Socket이 회수된다 (Socket은 무한하지 않은 유한 자원이다)
  6. 서버는 ACK를 받고, 연결을 종료한다 - CLOSED

TCP, UDP 헤더 형식

TCP Header 형식

  • 소스 포트: 데이터를 전송하는 소스의 포트 넘버
    • 16bit까지 표현 가능하므로 가능한 Port 번호는 0 ~ 65535인데, 0과 65535는 사용하지 않으므로 실제로 사용 가능한 포트는 65533개이다.
  • 목적지 포트: 목적지 컴퓨터의 포트 넘버 (16bit)
  • 시퀀스 넘버: 데이터를 어디까지 전송했는지를 나타내는 바이트 넘버
    • TCP 세그먼트가 여러 개의 세그먼트로 나뉘어져 보내질 수 있는데 이를, 시퀀스 넘버를 통해 순서대로 조합할 수 있다.
    • 첫 세그먼트가 1번이고, 데이터의 크기가 100바이트라면, 다음 세그먼트의 시퀀스 넘버는 101일 것이다.
  • Acknowledgement 넘버: 시퀀스 넘버를 받고 난 후, 기대하고 있는 시퀀스 넘버가 무엇인지를 나타냄.
  • Dataoffset: payload의 위치를 계산하기 위함
  • Flags: TCP 연결에서 상태를 나타내기 위한 플래그
  • Window Size: 보내는 컴퓨터와 받는 컴퓨터 간의 전송되는 데이터의 크기를 조정하는 부분
    • 앞선 'L3 핵심'이라는 포스트에서 TCP/IP 송/수신 구조에 대해 설명할 때 언급했다
    • 이 부분이 0이라면 Zero Window 장애가 발생하게 된다
  • Checksum: 헤더들이 제대로 전달되었는지 확인하는 부분
  • Data: 보내고 싶은 메시지

UDP Header 형식

TCP 헤더에 비해 매우 심플하다. 출발지와 목적지의 포트 번호, 길이, checksum만 존재한다.


상남자다.. UDP는 TCP와 달리 혼잡 제어 등에 관한 고민은 전혀 하지 않으며, 수신측의 Window Size 등 컨디션에 대한 고려를 전혀 하지 않는다. 단순히 데이터를 일방적으로 보낼 뿐이다.

 

이러한 UDP는 다음과 같은 특징이 있다.

  • 동영상이나 웹 스트리밍, 게임 서버 통신 등을 할 때 주로 사용한다.
  • Connectionless
  • 어플리케이션 소통이 안전하지 않다.
  • UDP는 데이터가 잘 전달되었는지 확인하지 않는다.
  • 그렇기에 제대로 전달이 안되었을 시 다시 보내지 않는다.
  • TCP 보다 빠르고 가벼움
    • TCP 헤더는 20바이트인 반면에 UDP 헤더는 8바이트 밖에 되지 않는다.
    • UDP는 데이터가 잘 전달되었는지 확인하지 않기 때문에 빠르다.

TCP vs. UDP

  • TCP의 경우,
    • 데이터가 제대로 전송되었는지 ACK 플래그를 통해 확인하는 과정이 필요했다.
    • 혼잡 제어 상황을 고려한다
  • 반면, UDP는 그런거 없다.
    • 요청이 오면 바로 응답이 전달된다.
      • 이 과정에서 응답이 제대로 전송되었는지 확인하는 과정이 없기에 굉장히 빠르다. => 멀티미디어 전송에 최적화되어 있다
      • 스트리밍의 경우, 버퍼링 등으로 데이터가 잠시 끊겼다가 정상적으로 돌아올 때, 끊긴 순간부터의 데이터를 보여주는 것이 아닌, 현재 스트리밍 되는 데이터를 보여준다.
      • 게임 서버의 경우, TCP 통신으로 구현했다면 통신이 느린 유저가 있을 경우, 데이터가 제대로 전송되지 않아 재전송하는 과정 등에 의해 해당 유저에 맞춰 모든 유저의 통신 속도가 하향 평준화될 것이다.. 이를 해소하기 위해 게임 서버는 보통 UDP 통신을 이용한다. 통신이 느려 게임이 끊기는 현상에 대한 책임을 게임 서버가 아닌 통신이 느린 유저에게 전가하는 것이다.
    • TCP의 혼잡 제어 로직을 애플리케이션 로직으로 직접 구현한다.

TCP '연결'이라는 착각

파일 다운로드 중 LAN 케이블을 분리했다가 다시 연결하면 TCP 연결은 어떻게 될까?

 

여기서 TCP 연결이라는 것은 L4 계층의 연결 의미하며, LAN 케이블의 분리는 L1 수준에서 물리적으로 연결을 끊은 것이다.
결론부터 말하면, L4 계층의 TCP 연결은 '유지'된다. 물론, LAN 선을 '얼마나 오래' 뽑힌 상태를 유지했느냐에 따라 다르긴 하다. 그리고 이 '얼마나 오래'는 또 운영체제마다 다르다. (물론 운영체제에 따라 연결이 완전히 복구되지  않을 수도 있다)

 

다음의 전화 상황 예시를 들어보자. 우리는 다음과 같은 상황을 겪어본 적이 있을 것이다.

  1. 늦은 밤 통화를 하며 내가 하소연을 하고 있는데, 상대방이 대답이 없다.. 상대방이 듣고있는지 확인하기 위해 "듣고 있어?"와 같은 말을 건넨다.
  2. 전화가 끊어졌는데, 끊어진줄도 모르고 계속해서 전화기에 대고 말을 하고 있었다.

1번 상황처럼 TCP 연결이 되었다고 하더라도, 상대방이 정말로 연결이 되어 있는지 확인해주는 작업이 필요한데, 이를 Hearbeat라고 한다. 이러한 Hearbeat를 보내는 주기는 RFC에서 표준을 정하고 있긴 하지만, 실제 운영체제마다 구현이 다르다. 때문에, Application 계층에서 소켓 프로그래밍을 한다면 'Hearbeat(연결 재확인?)'을 구현 해주어야 한다.

 

Heartbeat는 TCP 연결이 실제로 유지되는지 확인하기 위한 'keepalive' 패킷으로 생각할 수 있다. OS 수준에서 기본적으로 TCP Keepalive 기능을 제공하며, 이는 L4 수준에서 구현되는 기능이다. 여기에서 Heartbeat는 응용 계층에서도 직접 구현이 가능하며, 일반적으로 일정 간격으로 상대방의 응답을 체크해 연결을 확인한다.

 

2번 상황처럼 전화 연결이 끊어졌다고 하여 나의 대화가 끝나지 않은 것처럼, LAN 선이 뽑혔다고 해도 TCP 연결은 '일정 시간동안' 유지된다. 그렇다면 얼마나 오래 유지될까? 이는 RFC 문서에서 확인할 수 있다.

  • 재전송 타이머의 기본 근사 값은 대략 3초이다. 하지만, 대부분의 운영체제들은 1초 미만이다.
    • 예를 들면, 서버가 데이터를 보내면 클라이언트는 ACK를 보내야 하는데, 이 응답이 바로 안 올 수도 있다. 데이터가 안 오면 데이터를 재전송해야 하는데, 이를 기다리는 시간은 대략 3초 정도이다. 하지만, 대부분의 운영체제들은 1초 내로만 기다리고 데이터를 재전송한다
  • 재전송 타이머 만료 후에도 확인 응답을 받지 못한 경우, 세그먼트를 재전송하고 RTO(Retransmission Time-Out) 값은 두 배로 증가한다.
  • 예를 들어, 1초 > 2초 > 4초 > 8초 > 16초 간격으로 재전송한다.
  • 보통 최대 5회 재전송을 시도하고, 5회 이상 모두 실패할 경우 보통 전송 오류가 발생한다.

LAN 케이블을 뺐다가 1초 후에 다시 연결을 한다면 파일 다운로드가 수행되다가 잠시 멈추고, 다시 잘 수행될 것이다. 이는 L1 수준에서 케이블 단락이 일어나 연결을 할 수 없는 상태가 되었지만, L4의 '논리적 연결'은 유지되고 있었기 때문에 가능하다. 이때, 이 LAN 케이블을 빼는 행위를 '충격'이라고 볼 수 있다. 이러한 잠깐의 충격이 발생하더라도 서비스는 유지될 필요가 있다. 이러한 충격을 완화해주는 장치가 바로 앞서 계속 등장했던 '버퍼(Buffer)'이다. 버퍼는 데이터가 일시적으로 차단되었을 때 데이터를 임시 저장하는 기능을 하며, 이로 인해 애플리케이션에서 연결이 끊긴 듯 보이지 않도록 한다.

 

사실 이러한 '충격'은 무선 랜 상황에서는 와이파이가 끊기거나 엘리베이터에 타서 통신이 안되는 상황 등등 매우 빈번하게 일어난다. 이렇게 연결이 '잠깐' 끊긴 상황 속에서도 우리가 서비스를 이용할 수 있는 이유는 버퍼가 충격을 완화해주기 때문이다.

 

=> '연결'이라는 것은 '착각'이다..
=> '연결'은 사실 End-point의 주관적인 판단에 불과하다.
=> '연결'은 보안성이 없다. 기밀성 + 무결성 + 가용성이 없다..

A와 B가 TCP 통신을 할 때, A는 실제로 B와 통신을 하고 있다는 것을 단정할 수 없다. 실제로 B가 아니라 B와 똑같은 행동을 하는 C와 통신을 하고 있었을 수도 있다.. C는 3-way handshaking 시 올바른 SYN과 ACK만 보내준다면 A는 감쪽같이 속을 것이다.

A와 B가 TCP 통신을 하는 상황에서, A가 특정 행동을 수행하자마자 LAN 선을 뽑고, B는 이와 다른 행동을 한다면.. A의 LAN 선이 연결되었을 때 A와 B가 서로 다른 상태값을 가질 수도 있다.. (ex. 과거의 아이템 복제 버그) 다행히 지금은 이런 짓은 안 통한다.

728x90
반응형