CS/네트워크

[네트워크 개념 복습] TCP

Basaeng 2026. 9. 10. 15:28

 

오늘은 TCP에 대해 정리해보려고합니다.

 

내용은 먼저 저의 생각을 적은 뒤 이후 ai로 한번 정제해서 전달하는 방식입니다.

 

TCP는 Trasnmission Control Protocol로 OSI의 Transport 계층의 프로토콜입니다.

 

TCP를 이해하기 위해서는 TCP의 헤더에 대해 알아야합니다.

 

  0                   15                  31
  +-------------------+-------------------+
  |   Source Port     | Destination Port  |
  +-------------------+-------------------+
  |              Sequence Number          |
  +---------------------------------------+
  |           Acknowledgment Number        |
  +----+------+----------------+-----------+
  |Hdr |Flags |  Window Size   |           |
  |Len |      |                |           |
  +----+------+----------------+-----------+
  |   Checksum        | Urgent Pointer     |
  +-------------------+-------------------+
  |     Options (있을 수 있음)              |
  +---------------------------------------+
  |              Payload                  |
  +---------------------------------------+

 

Port: 기본적으로 TCP는 Port식별이 가능합니다. port는 하나의 PC안에서 특정 프로그램을 찾는 주소입니다.

 

Sequence Number: Sequence Number는 TCP가 순서를 보장하는 프로토콜임을 알 수 있는 데이터입니다.

TCP는 TCP패킷이 조각나서 도착하는 경우 target에서 재조립하여 상단으로 전달시킵니다.

 

Acknowledgment Number: ACK 번호를 담습니다. 이를 통해 TCP가 신뢰성을 보장하는 프로토콜임을 알 수 있습니다.

 

Offset, Length: 현재 패킷의 길이와 전체 패킷에서의 위치를 나타냅니다.

 

Window Size: 현재 TCP에서 사용할 수신윈도우의 크기를 나타냅니다. 이를통해 TCP는 흐름제어를 하고있음을 알 수 있습니다.

 

Flags: SYN, ACK, FIN, RST, PSH, URG등 현재 TCP 패킷의 제어역할이 필요할 때 사용합니다.


자주나올만한 질문

 

1. TCP 3-way handshake에 대해 설명하고 왜 3-way인지 말하시오

나의 답 : TCP 3-way handshake는 TCP에서 송신측과 수신측이 연결을 수립하는 과정입니다.

먼저 Server측이 SYN을 보내 seq번호를 알리고 Client측은 SYN과 ACK를 보내 Client자신의 seq num과 앞서받은 신호에 대한 ACK응답을합니다. 마지막으로 Server측은 받은 syn에 대한 ack를 보내며 마칩니다.

 

기본적으로 송신측과 수신측이 모두 정상적으로 데이터를 교환했는지를 인지하기 위해서는 보낸 SYN에 대한 ACK를 받아야합니다. 이 과정을 마치기 위해 3번의 통신이 필요합니다.

 

AI의 평가: 6/10입니다. Server가 SYN을 보낸다고 시작한 부분이 가장 큽니다. 일반적인 능동 연결에서는 보통 Client가 SYN을 보내고 Server가 SYN+ACK, Client가 ACK를 보냅니다. 그리고 3-way인 이유도 단순히 “둘 다 ACK를 받아야 해서”보다는, 양방향 통신 가능 여부와 양측 초기 Sequence Number를 서로 확인하기 위해서라고 설명하는 편이 더 정확합니다.

 

2. TCP 연결 종료는 왜 4-way Handshake인가요? 3-way로 종료하면 안 되나요?

나의 답: 기본적으로 4-way handshake = graceful shutout은 클라이언트가 서버에게 능동적으로 TCP연결을 종료함을 통보하는 것입니다. 서버는 FIN을 받았을 때 현재 클라이언트에게 보내고 있었던 데이터들을 전부 보내야하기 때문에 ACK와 FIN을 분리해서 보내야합니다. 따라서 총 4번의 데이터 전송이 필요합니다.

 

AI의 평가: 8/10입니다. 핵심은 잘 잡았습니다. FIN을 받았다고 해서 상대 방향 송신까지 즉시 끝나는 것이 아니므로 ACK와 FIN이 분리될 수 있다는 설명이 좋습니다. 다만 graceful shutout보다는 graceful shutdown이고, “클라이언트가 서버에게 종료 통보”로 한정하지 말고 어느 쪽이든 active close를 시작할 수 있다고 하면 더 좋습니다.

 

3. TCP에서 패킷 하나가 유실되거나 순서가 뒤바뀌어 도착하면 어떻게 처리하나요? :

나의 답: 유실되거나 순서가 바뀌는 경우 수신측의 TCP software에서 감지하고 정상적인 데이터를 기다립니다. 송신측은 보낸 데이터에 대한 ACK가 돌아오지않았다면 해당 패킷이 유실된것이기 때문에 해당 패킷부터 재전송하게 됩니다.

 

AI의 평가: 6/10입니다. 큰 방향은 맞습니다. 하지만 “ACK가 안 오면 해당 패킷부터 재전송”은 너무 단순합니다. 실제로는 RTO timeout, Duplicate ACK, Fast Retransmit, SACK 같은 메커니즘이 있고, 순서가 뒤바뀌면 수신 측은 out-of-order 데이터를 버퍼링할 수 있습니다. 이 질문은 보통 TCP 신뢰성 메커니즘을 얼마나 정확히 아는지 보려는 질문이라 현재 답은 조금 얕습니다.

 

4. TIME_WAIT이 수만개쌓였습니다 왜일까요 :

나의 답 : TIME_WAIT의 경우 모든 통신이 종료되었지만 해당 포트를 통해 아직 네트워크에 떠도는 패킷이 들어올 수도 있기 때문에 해당 포트를 정해진 시간만큼 점유해 예방하는 방식입니다. 다만 TIME_WAIT는 포트를 점유하기 때문에 수만개 쌓인다면 새로운 연결을 수립할 수 없는 장애가 생길 수 있습니다.

 

AI의 평가: 7/10입니다. TIME_WAIT의 목적 중 지연된 이전 세그먼트가 새 연결에 섞이는 것을 방지한다는 설명은 맞습니다. 다만 “해당 포트를 점유해서 수만 개 쌓이면 새로운 연결을 수립할 수 없다”는 표현은 과합니다. 정확히는 특정 4-tuple 재사용 제한과 ephemeral port 고갈 문제가 생길 수 있다는 쪽이 좋습니다. 서버 전체가 무조건 새 연결을 못 받는다고 단정하면 감점입니다.

 

5. TCP 서버를 만들었는데 한 클라이언트가 보낸 메시지가 recv() 한 번에 정확히 한 메시지씩 들어온다고 보장할 수 있나요?

나의답: 아닙니다 TCP는 경계가 존재하지 않고 recv는 현재까지 쌓인 데이터를 모두 가져오는 것이기 때문에 해당 데이터 사이에서의 순서는 보장됨에도 불구하고 보낸 메세지가 완성된 하나라고 볼 수는 없습니다.

 

AI의 평가: 8.5/10입니다. 가장 좋습니다. TCP는 메시지 경계가 없는 byte stream이고 순서는 보장되지만 send()와 recv()가 1:1 대응하지 않는다는 핵심을 잘 잡았습니다. 다만 recv는 현재까지 쌓인 데이터를 모두 가져온다는 표현은 정확하지 않습니다. recv()는 버퍼 크기와 현재 가용 데이터에 따라 일부만 반환할 수 있습니다. 여기에 length-prefix나 delimiter 같은 framing을 언급하면 거의 만점입니다.


AI가 출력한 모범답변

1. TCP 3-way handshake는 TCP 연결을 수립하면서 양쪽이 서로 통신 가능한 상태인지 확인하고, 각자의 초기 Sequence Number를 동기화하는 과정입니다.
일반적으로 Client가 SYN과 자신의 초기 Sequence Number를 보내고, Server가 이를 확인한 뒤 SYN + ACK로 자신의 초기 Sequence Number와 Client SYN에 대한 ACK를 보냅니다. 마지막으로 Client가 Server의 SYN에 대한 ACK를 보내면 연결이 성립합니다.

3-way가 필요한 이유는 양방향 모두에 대해 송수신 가능 여부를 확인해야 하기 때문입니다. 2-way만으로는 Server가 보낸 SYN을 Client가 정상적으로 수신했는지 Server가 확인할 수 없습니다.

 

2. TCP는 양방향 통신이 서로 독립적으로 종료될 수 있기 때문에 일반적으로 4-way handshake를 사용합니다.

한쪽이 FIN을 보내면 상대방은 먼저 ACK를 보내 FIN을 받았음을 알립니다. 하지만 상대방은 아직 전송해야 할 데이터가 남아 있을 수 있기 때문에 즉시 FIN을 보내지 않을 수도 있습니다. 이후 자신의 송신이 끝났을 때 별도로 FIN을 보내고, 마지막으로 그 FIN에 대한 ACK를 받으면서 종료됩니다.

따라서 일반적으로 FIN → ACK → FIN → ACK의 4단계가 됩니다. 다만 상황에 따라 ACK와 FIN을 하나의 세그먼트로 함께 보내면 3개의 세그먼트처럼 보일 수도 있습니다.

 

3. TCP는 Sequence Number를 이용해 데이터의 순서와 누락 여부를 판단합니다.

순서가 뒤바뀐 경우 수신 측 TCP는 먼저 도착한 이후 데이터를 버퍼링하고, 아직 도착하지 않은 Sequence Number를 ACK로 계속 알릴 수 있습니다.

패킷이 유실되면 송신 측은 RTO timeout이 발생하거나 Duplicate ACK를 여러 번 수신하는 등의 방법으로 손실을 감지하고 재전송합니다. 특히 Duplicate ACK가 일정 횟수 발생하면 Fast Retransmit을 수행할 수 있습니다.

SACK이 활성화되어 있다면 수신 측은 이미 받은 구간도 알려줄 수 있어서 송신 측이 필요한 구간만 효율적으로 재전송할 수 있습니다.

 

4. TIME_WAIT은 TCP 연결에서 active close를 수행한 쪽이 마지막 ACK를 보낸 후 일정 시간 동안 연결 정보를 유지하는 상태입니다.

목적은 크게 두 가지입니다. 첫째, 마지막 ACK가 유실되었을 때 상대방이 FIN을 재전송하면 다시 ACK를 보낼 수 있도록 하기 위해서입니다. 둘째, 이전 연결에서 지연된 패킷이 동일한 4-tuple을 사용하는 새로운 연결에 섞이는 것을 방지하기 위해서입니다.

TIME_WAIT이 수만 개 쌓였다면 짧은 TCP 연결을 매우 자주 생성하고 종료하고 있을 가능성이 높습니다.

클라이언트처럼 outbound connection을 많이 생성하는 환경에서는 ephemeral port가 부족해져 새로운 연결 생성에 문제가 생길 수도 있습니다.

 

5. 보장할 수 없습니다. TCP는 메시지 기반 프로토콜이 아니라 byte stream 기반 프로토콜이기 때문입니다.

예를 들어 Client가 send()를 두 번 호출해 "ABC"와 "DEF"를 보냈더라도 서버의 recv()에서는 "ABCDEF"가 한 번에 들어올 수도 있고 "AB", "CDEF"처럼 여러 번에 나뉘어 들어올 수도 있습니다.

TCP가 보장하는 것은 byte의 순서이지 애플리케이션 메시지의 경계가 아닙니다.

따라서 서버에서는 별도의 message framing이 필요합니다. 예를 들어 헤더에 payload length를 넣는 length-prefix 방식이나 delimiter 방식을 사용할 수 있습니다.

 


이후 든 생각

생각해보니 TIME_WAIT의 경우는 매번 포트가 새로 생긴다고 하기 애매한게

listen socket에서 새로운 연결이 수립되고 만들어진 소켓이 각각 새로운 포트를 가지는게 아니기 때문에 이 때는 포트 자체의 소진이라고 말하기 보다는 TCP소켓 정보가 논페이지드 풀 메모리에 사용되기 때문에 누적되면 메모리가 고갈되어 문제를 일으킬 수 있고, 이에 대한 해결방법으로 LINGER옵션을 통해 l_linger = 0으로 하여 RST패킷을 보내는 방법이 있다는걸 언급하는 걸 까먹었었네요