핵심 인사이트 (3줄 요약)
- 본질: TCP 통신을 할 때 데이터가 내 프로그램(롤, 크롬)에서 랜카드로 곧바로 다이렉트로 쏴지는 것이 아니라, 반드시 운영체제가 관리하는 가상 창고인 '송신 버퍼(Send Buffer)'와 '수신 버퍼(Receive Buffer)'라는 중간 정거장을 거쳐 포장되고 조립되는 비동기 처리 시스템을 거친다.
- 송신 버퍼 (보내는 쪽 창고): 애플리케이션이 "야 이거 1GB 보내!"라고 던지면 일단 여기에 쟁여놓고, TCP가 눈치껏(윈도우 사이즈와 MTU를 보며) 1460바이트씩 예쁘게 썰어서 내보내는 도마(작업대) 역할을 한다.
- 수신 버퍼 (받는 쪽 창고): 밖에서 들어온 1460바이트짜리 조각들을 차곡차곡 모아두었다가, 순서(Seq)가 1, 2, 3번으로 완벽하게 맞춰지면(Reassembly) 그제야 애플리케이션(크롬)에게 "자, 완성된 텍스트야 가져가!"라고 넘겨주는 보관소 역할을 한다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: TCP가 바이트 스트림 통신을 보장하고 흐름 제어 및 오류 제어를 수행하기 위해 커널(OS) 메모리 공간에 할당하는 송신(Send) 및 수신(Receive)용 임시 저장 공간(Queue/Buffer).
-
필요성: 만약 버퍼가 없다면 어떨까? 내가 엑셀 파일 10MB를 저장 버튼을 눌러 인터넷으로 쏜다. 내 엑셀 프로그램은 랜카드가 그 10MB를 1460바이트씩 잘라서 미국 구글에 도착하고 영수증(ACK)이 다 돌아올 때까지(몇 초 소요), 화면이 하얗게 굳어서(응답 없음) 다른 마우스 클릭조차 못 하고 마냥 기다려야 한다. 이 동기화의 끔찍함을 막기 위해, "프로그램아, 넌 그냥 10MB를 OS 창고(송신 버퍼)에 휙 던져두고 넌 바로 하던 일(마우스 클릭) 계속해! 내가 창고에서 알아서 조금씩 빼서 택배 부치고 영수증 챙길게!"라는 비동기화(Asynchronous) 쿠션이 버퍼의 절대적 존재 이유다.
-
💡 비유: 버퍼는 배달의 민족 **"라이더 픽업 존(선반)"**과 같습니다.
- 송신 버퍼: 주방장(애플리케이션)이 햄버거 100개를 순식간에 다 만들어서 배달 선반(송신 버퍼)에 쌓아둡니다. 주방장은 바로 퇴근합니다. 라이더(TCP)는 선반에 있는 햄버거를 오토바이 통에 들어가는 만큼만(Window Size) 조금씩 빼서 알아서 배달합니다.
- 수신 버퍼: 배달된 햄버거 세트의 감자튀김, 콜라, 햄버거 조각들이 도착하는 대로 경비실 보관함(수신 버퍼)에 쌓입니다. 경비원은 콜라만 왔다고 손님(어플리케이션)을 부르지 않고, 세트가 완벽히 다 모여야만 손님에게 "가져가세요"라고 연락합니다.
📢 섹션 요약 비유: 송수신 버퍼는 급성질인 **"사장님(어플리케이션)"**과 일처리가 느린 "거래처(네트워크)" 사이에서, 양쪽이 스트레스받지 않도록 서류를 대신 쌓아두고 스케줄을 조율해 주는 **"능구렁이 비서(운영체제)"**의 책상 서랍입니다.
Ⅱ. 버퍼의 연동 메커니즘과 흐름 제어 (Deep Dive)
TCP 통신은 본질적으로 **"내 송신 버퍼의 물을 상대방 수신 버퍼로 넘겨붓는 과정"**이다.
1. 송신 버퍼 (Send Buffer)의 라이프 사이클
- 적재: 어플리케이션 계층이 1MB 데이터를
write()또는send()함수를 통해 송신 버퍼에 콸콸 들이붓는다. - 전송: TCP 스케줄러가 상대방의 빈 공간(Window Size)이 14600바이트임을 확인하고, 1460바이트짜리 세그먼트 10개로 썰어서 IP 계층(밖)으로 던진다.
- 잔류 (재전송 대기): 밖으로 던졌다고 버퍼에서 데이터를 지울까? 절대 안 지운다! 혹시 가다가 바다에 빠질 수 있으므로, 상대방의 영수증(ACK)이 올 때까지 복사본을 버퍼에 꼭 쥐고 있는다.
- 삭제: "야! 10개 잘 받았어!(ACK)"라는 영수증이 당도하면 그제야 송신 버퍼에서 10개의 데이터를 빗자루로 쓸어내 버리고 텅 빈 공간을 확보한다. (이때 슬라이딩 윈도우가 한 칸 전진한다).
2. 수신 버퍼 (Receive Buffer)와 Window Size의 탄생
흐름 제어(Flow Control)의 주어는 수신 버퍼다.
- 수신: 밖에서 조각난 1460바이트짜리 패킷들이 수신 버퍼로 툭툭 떨어져 쌓인다.
- 조립: 1번, 2번, 3번이 순서대로 도착했다. OS는 이걸 예쁘게 하나로 이어 붙인다. 만약 2번이 안 오고 1번, 3번이 오면 조립을 못 하고 버퍼에 임시로 놔둔 채 2번을 다시 달라고 소리친다(중복 ACK).
- 반출: 조립이 끝난 덩어리를 수신 쪽 애플리케이션(크롬 브라우저)이
read()함수를 써서 쑥 빼간다. 그러면 버퍼에 다시 빈 공간이 생긴다. - 마법의 계산 (Window Size): 수신자 TCP는 매번 영수증을 쏠 때 자기 버퍼 상태를 체크한다. "내 수신 버퍼의 전체 크기(예: 64KB) - 현재 조립 못 하고 쌓여있는 데이터 = 남은 빈 공간(예: 20KB)". 이 20KB를
Window Size칸에 적어서 송신자에게 던져주는 것이다.
┌─────────────────────────────────────────────────────────────┐
│ 송신 버퍼와 수신 버퍼의 물 붓기 핑퐁 구조 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ 어플리케이션 A (송신) ] [ 어플리케이션 B (수신) ]│
│ │ (콸콸콸) ▲ (야금야금 빼감) │
│ ▼ │ │
│ [ 송신 버퍼 (가득참) ] [ 수신 버퍼 (점점참) ] │
│ │ ▲ │
│ └─── (남은 공간만큼만 TCP 패킷 발송!) ───────┘ │
│ │
│ * 딜레마: 어플 B가 롤(LOL) 하느라 바빠서 수신 버퍼에서 데이터를 안 빼감.│
│ * 방어: 수신 버퍼가 100% 꽉 차버림! ──▶ 수신자가 [Window=0] 발송! │
│ * 결과: 송신 버퍼는 전송을 강제 중지하고, 어플 A가 붓는 물도 차단함. │
│ │
│ ▶ "이것이 두 톱니바퀴(버퍼)가 물려 돌아가는 Flow Control의 미학이다!"│
└─────────────────────────────────────────────────────────────┘
📢 섹션 요약 비유: 송신/수신 버퍼는 댐과 댐을 잇는 **"저수지"**입니다. 비가 미친 듯이 와서 상류 댐(송신 버퍼)에 물이 가득 차도, 하류 댐(수신 버퍼)에 빈 공간이 없으면 절대 수문을 열지 않음으로써, 마을(어플리케이션)이 홍수에 떠내려가는 재앙을 완벽하게 통제해 냅니다.