우리가TCP vs UDP에서는 TCP를 꼼꼼한 친구 정도로 먼저 소개했고, TCP 3-way handshake에서는 그 꼼꼼함이SYN,ACK, 포트 번호라고 부르는 건, 사실 20바이트짜리 격자 안에 꽉 들어찬 서로 다른 칸들이에요.
SYN, ACK, sequence 번호 같은 실제 숫자와 신호로 보인다는 걸 봤어요. 그리고 TCP 재전송과 신뢰성에서는 그 번호들이 빠진 조각을 다시 챙길 때 어떻게 쓰이는지도 봤죠.
근데 여기서 이런 생각이 들지 않으세요?
“좋아요, 이제 감은 왔어요. 근데 그 숫자들이 TCP 헤더 안에서는 정확히 어디 칸에 들어가는데요?”근데 이 글이 진짜 필요한 순간은 보통 더 실전 쪽에서 와요.
Flags [S.]는 보이는데 왜 ACK가 1 늘었는지 헷갈릴 때- Wireshark에서
Window,Checksum,Options가 보여도 각 칸의 역할이 안 붙을 때 - 재전송, 흐름 제어, 핸드셰이크 설명을 읽었는데 그 숫자들이 실제 헤더 어디에 앉아 있는지 감이 없을 때
여기서는 TCP 기본 헤더와 자주 보는 옵션까지를 다뤄요. 혼잡 제어 알고리즘 전체나 상태 머신 전체는 오늘 다 안 열어요. 지금은 “핸드셰이크와 재전송에서 보던 숫자가 실제 헤더의 어느 칸이었는지” 를 선명하게 만드는 데 집중할게요.
그래서 TCP 헤더는 한마디로 뭐예요?
TCP 헤더는 단순한 주소표가 아니라, 연결의 상태와 데이터 흐름을 계속 맞춰가기 위한 운영 메모에 더 가까워요. 즉 TCP는 이 헤더 안에서 계속 이런 걸 같이 말하고 있는 거예요.- 어느 포트에서 어느 포트로 가는지
- 지금 바이트 흐름의 어디쯤 이야기하고 있는지
- 상대가 다음엔 어디부터 주길 기다리는지
- 연결 시작 / 확인 / 종료 / 리셋 같은 상태 신호가 켜졌는지
- 지금 얼마나 더 받아둘 수 있는지
즉 이 글은 헤더 안에 뭐가 들어 있나를 외우는 글이 아니라, 기본편에서 따로따로 보던 핸드셰이크 / ACK / 재전송 / 윈도우 이야기가 사실은 한 장의 헤더 안에 같이 살고 있었다는 걸 확인하는 글이에요.
기본편에서는 이걸 “인사한다” 정도로 읽었죠. 오늘은 그 인사 안에 들어 있던 포트, 번호, 플래그, 옵션이 헤더 어느 줄에 있는지까지 펼쳐볼 거예요. 그리고 왜 TCP가 이렇게 칸이 많아질 수밖에 없었는지도 같이 잡아볼게요.
TCP 기본 헤더 전체 그림
TCP 기본 헤더는 최소 20바이트(160비트) 예요. 한 줄을 32비트로 그리면 딱 5줄이에요. 옵션이 붙으면 더 길어질 수 있지만, 기본 뼈대는 늘 이 5줄이에요. 이 그림에서 먼저 잡아야 할 감각은 세 가지예요.- 기본 헤더는 20바이트라는 점
Sequence Number와Acknowledgment Number가 각각 한 줄 전체를 통째로 쓴다는 점- 플래그는 1비트씩 짧지만, 연결의 상태를 읽을 때는 엄청 중요하다는 점
Data Offset 필드가 정확히 가리켜줘요.
1번째 줄 — 어느 창구에서 어느 창구로
Source Port (16비트) · Destination Port (16비트)
포트와 소켓에서 봤던 “같은 집 안에서 어느 방으로 보낼까” 감각이 바로 여기 있어요. IP가 어느 집까지 갈지를 맡는다면, TCP 포트는 그 집 안 어느 서비스까지 갈지를 맡아요.
이 줄이 16비트 + 16비트로 나뉜 이유도 단순해요. 포트 번호 범위가 0~65535 여야 하니까요. 한쪽은 출발지, 다른 한쪽은 목적지예요. 그래서
192.168.0.10:51515 → 142.250.196.78:443 같은 한 연결은, 사실 IP 주소 2개 + 포트 2개 조합으로 식별돼요.
2·3번째 줄 — 몇 번째 바이트부터고, 다음엔 뭘 기다릴까요?
Sequence Number (32비트) · Acknowledgment Number (32비트)
여기가 TCP의 핵심이에요. TCP 3-way handshake와 TCP 재전송과 신뢰성에서 계속 보던 숫자들이 바로 이 두 줄이에요.
가장 중요한 포인트는 이거예요.
Sequence Number는 패킷 번호가 아니라 바이트 흐름의 시작 위치예요.Acknowledgment Number는 “여기까지 받았어” 보다 더 정확히는 “다음에는 이 번호부터 줘” 예요.
SYN 이 켜져 있으면, 그 세그먼트의 Sequence Number 자체가 초기 시작 번호(ISN) 가 되고, 실제 첫 데이터 바이트는 ISN+1 부터로 봐요. 그래서 핸드셰이크에서 SYN seq=1000 다음에 ack=1001 이 나오는 거예요.
비슷한 이유로 FIN 도 sequence 공간을 1칸 써요. 그래서 연결을 닫는 장면에서도 “데이터는 없는데 왜 ACK가 1 늘었지?” 같은 모습이 나올 수 있어요.
즉, 기본편에서 “숫자를 맞춘다” 라고 했던 게 추상적인 말이 아니라, 진짜로 헤더 2줄을 통째로 써서 바이트 흐름의 기준점을 맞춘다는 뜻이에요.
4번째 줄 — 헤더 길이, 플래그, 그리고 지금 얼마나 더 받을 수 있을까요?
Data Offset (4비트) · Reserved (4비트) · Flags (8비트) · Window (16비트)Data Offset — 헤더가 어디서 끝나는지
이 필드가 “TCP 데이터 본문은 몇 번째 바이트부터 시작하는가” 를 알려줘요. 4비트밖에 안 되니까 0~15까지밖에 못 적죠. 그래서 TCP는 길이를 바이트가 아니라 32비트 단어 개수로 적어요.5면5 × 4 = 20바이트→ 옵션 없는 기본 헤더10이면10 × 4 = 40바이트→ 옵션이 20바이트 붙은 헤더
Flags — 연결의 분위기를 1비트씩 찍어두는 칸
여기서 중요한 건 이 자리가 8비트짜리 제어 비트맵이라는 점이에요. 즉,Sequence Number 나 Window 처럼 숫자 하나를 담는 칸이 아니라, 연결 시작 / 확인 / 종료 / 리셋 같은 신호를 1비트씩 켜서 표시하는 칸이죠.
이 글에서는 플래그가 TCP 헤더 4번째 줄 어디에 붙어 있는지까지만 잡고 갈게요. SYN, ACK, FIN, RST, PSH 같은 이름이 각각 무슨 분위기로 읽히는지, 그리고 Flags [S], Flags [S.], Flags [F.] 같은 축약 표기가 실제 캡처에서 어떻게 보이는지는 심화편 TCP 플래그는 어떻게 읽어야 할까요? 에서 따로 자세히 열어볼 수 있어요.
Window — “나는 지금 이만큼 더 받을 수 있어요”
이 16비트는 수신 버퍼 여유를 광고하는 칸이에요. 즉, “다음에는 이 번호부터 줘” 만 말하는 게 아니라, “그리고 지금은 이 정도까지는 더 흘려 보내도 돼” 도 같이 말하는 거죠. 여기서는 범위만 짧게 나눠둘게요.여기서는 Window 필드 자체가 헤더 어디에 있는지까지만 볼게요. 실제로 이 값이 흐름 제어와 Window Scale 옵션을 만나서 어떻게 커지는지는 TCP 윈도우와 흐름 제어는 왜 같이 읽어야 할까요?에서 이어서 열어볼게요.RFC 7323은 이 16비트 한계 때문에 Window Scale 옵션을 도입해요. 중요한 건, 와이어 위의 Window 필드는 여전히 16비트라는 점이에요. 다만 양쪽이 핸드셰이크 때 합의한 배율을 곱해서 실제 의미를 키우는 거예요.
5번째 줄 — 체크섬과 급한 표시
Checksum (16비트) · Urgent Pointer (16비트)Checksum 은 “헤더만 대충 보자” 가 아니에요. RFC 9293 3.1절 기준으로 TCP 체크섬은 TCP 헤더 + TCP 데이터 뿐 아니라, IP의 출발지/도착지 주소와 프로토콜 번호를 끌어온 pseudo-header 까지 함께 계산해요. 그래서 잘못된 주소로 흘러간 세그먼트까지 어느 정도 잡아낼 수 있어요.
그리고 TCP에서 체크섬은 선택 사항이 아니에요. RFC 9293은 송신자가 반드시 만들고, 수신자가 반드시 검사해야 한다고 못 박아요.
반면 Urgent Pointer 는 초심자 입장에서는 자주 볼 일이 거의 없어요. URG 플래그가 켜졌을 때만 의미가 있고, 현대의 일반적인 웹 트래픽에서는 대부분 0 으로 지나가요. 그래서 이 칸은 “있긴 있는데 실전 웹 패킷에서는 거의 조용하다” 정도로 먼저 기억해도 충분해요.
6번째 줄 이후 — SYN 패킷에서 특히 자주 보이는 옵션들
기본 헤더 20바이트만으로도 TCP는 동작해요. 근데 실제 인터넷에서는 핸드셰이크 때 추가 규칙을 같이 맞추는 일이 많아요. 그 메모장이 바로 Options예요.
RFC 9293 3.2절은
EOL, NOP, MSS 를 기본 옵션으로 설명하고, RFC 9293 3.2.1절은 SACK, Timestamp, Window Scale 을 오늘날 흔한 옵션으로 짚어요. 그중 Window Scale 과 Timestamp 의 자세한 의미는 RFC 7323이 더 풀어줘요.
여기서 중요한 감각은 이거예요.
- 기본 헤더는 연결 운영의 공통 뼈대
- 옵션은 그 연결에서만 추가로 합의한 규칙
근데 왜 굳이 이렇게 칸이 많을까요?
TCP가 단순히 “데이터를 빨리 보낸다” 만 목표였다면 훨씬 짧아도 됐을 거예요. 근데 TCP는 그보다 더 많은 걸 맡아요.1. 패킷이 아니라 바이트 흐름을 맞춰야 하니까요
TCP는 “패킷 7번” 보다는 “전체 바이트 흐름 중 1201번째부터” 를 더 중요하게 봐요. 그래서Sequence Number 와 Acknowledgment Number 가 각각 32비트씩 통 크게 자리를 차지해요.
2. 연결 상태와 데이터 전송을 같은 헤더 안에서 같이 다뤄야 하니까요
SYN, ACK, FIN, RST 같은 플래그는 데이터 본문이 없어도 연결의 큰 전환점을 알려줘야 해요. 그래서 제어 신호와 데이터 흐름 정보가 한 헤더 안에 같이 살아요.
3. 받는 쪽 여유를 계속 광고해야 하니까요
보내는 쪽이 신나게만 보내면 안 되죠. 받는 쪽이 “지금은 여기까지 받아둘 수 있어” 라고 계속 말해줘야 흐름 제어가 가능해요. 그 역할이Window 예요.
4. 연결마다 규칙이 조금씩 달라질 수 있으니까요
MSS, Window Scale, Timestamp 같은 옵션은 연결마다 다를 수 있어요. 그걸 기본 헤더에 다 박아두는 대신, 필요할 때만 뒤에 붙이는 확장 메모지로 만든 거예요.실제 패킷에서 이렇게 보여요
말로만 보면 여전히 추상적이니까, 이번엔 SYN 세그먼트 하나를 실제 바이트와 사람이 읽는 한 줄로 같이 볼게요.먼저, 진짜 바이트로 보면
설명용으로 단순화한 SYN 세그먼트의 TCP 헤더를 보면 이렇게 생겼다고 해볼게요.
여기서 제일 중요한 건 세 가지예요.
a0의 상위 4비트a가Data Offset = 10이라서, 이 SYN은 헤더만 40바이트라는 점02가 SYN 플래그라는 점- 기본 20바이트 뒤에 붙은 옵션들 때문에, 우리가 평소 보는 SYN 패킷은 생각보다 헤더가 길다는 점
사람이 읽는 한 줄로 보면
도구가 이걸 풀어주면 보통 이런 식으로 보여요..51515 > .443— 1번째 줄의 Source Port / Destination PortFlags [S]— 4번째 줄의 SYN 비트seq 305419896— 2번째 줄의 Sequence Numberwin 64240— 4번째 줄의 Windowoptions [...]— 6번째 줄 이후의 Options
잘못 읽기 쉬운 함정 네 가지
하나, Sequence Number를 패킷 번호처럼 읽기. 아니에요. TCP는 바이트 흐름을 세요. 100바이트를 보내면 sequence가 100만큼 앞으로 가요. 둘, ACK를 “이 패킷 받았어요” 정도로만 읽기. 더 정확히는 “다음엔 이 번호부터 줘” 예요. 그래서 누적 ACK처럼 동작해요. 셋, Data Offset을 바이트 수로 착각하기.5 는 5바이트가 아니라 5 × 4바이트 = 20바이트 예요. 이걸 틀리면 데이터 시작 위치를 완전히 잘못 읽게 돼요.
넷, Window 필드가 실제 윈도우 크기 그 자체라고 단정하기.
핸드셰이크에서 Window Scale 을 합의했다면, 와이어 위 16비트 값에 배율을 곱해 해석해야 해요.
자, 정리해볼까요?
- TCP 기본 헤더는 최소 20바이트(160비트), 옵션까지 붙으면 최대 60바이트예요.
- 1줄: Source Port + Destination Port — 어느 서비스끼리 이야기하는지.
- 2줄: Sequence Number, 3줄: Acknowledgment Number — 바이트 흐름의 위치와 다음 기대 위치.
- 4줄: Data Offset + Flags + Window — 헤더 길이, 연결 신호, 수신 여유.
- 5줄: Checksum + Urgent Pointer — 무결성 검사와 드물게 쓰는 긴급 표시.
- 6줄 이후: MSS, Window Scale, Timestamp 같은 옵션 — 연결별 추가 규칙.
이어서 보면 좋은 글
- 이 TCP 헤더가 어떤 IP 헤더 바로 뒤에 얹히는지 다시 보고 싶다면 — IPv4 헤더 한 줄 한 줄 읽기
SYN,SYN-ACK,ACK가 이 칸들을 어떻게 채우는지 흐름부터 다시 보고 싶다면 — TCP 3-way handshake는 왜 세 번이나 주고받을까요?Sequence Number와ACK가 재전송에서 어떻게 활약하는지 바로 이어서 보고 싶다면 — TCP 재전송과 신뢰성- 이 헤더가 실제 캡처 화면에서 어떤 줄로 보이기 시작하는지 바로 이어서 보고 싶다면 — tcpdump 한 줄은 어떻게 읽어야 할까요?