TCP는 데이터를 그냥 보내고 끝내는 방식이 아니다.
보낸 데이터가 잘 도착했는지 확인하고, 문제가 생기면 다시 보내면서 통신의 신뢰성을 높이는 전송 계층 프로토콜이다.
지난 글에서 TCP는 데이터를 보내기 전에 연결을 만든다고 정리했다.
그런데 연결을 만들었다고 해서 데이터가 항상 안전하게 도착하는 것은 아니다.
네트워크에서는 패킷이 중간에 사라질 수도 있고, 순서가 뒤바뀔 수도 있고, 받는 쪽이 너무 바빠서 데이터를 감당하지 못할 수도 있다.
TCP는 이런 상황을 고려해서 데이터를 보낸다.
여기서 중요한 건 TCP가 단순히 “보냈다”가 아니라,
“상대가 제대로 받았는지까지 확인한다”는 점이다.
TCP는 데이터를 보낼 때 각 데이터에 번호를 붙인다.
이 번호를 순서 번호라고 한다.
예를 들어 서버가 클라이언트에게 긴 데이터를 보내야 한다고 해보자.
이 데이터를 한 번에 통째로 보내는 것이 아니라 여러 조각으로 나누어 보낸다.
그런데 네트워크를 지나가다 보면 먼저 보낸 조각이 늦게 도착하고, 나중에 보낸 조각이 먼저 도착할 수도 있다.
받는 쪽 입장에서는 이런 상황이 된다.
“어? 두 번째 조각이 먼저 왔네?”
“첫 번째 조각은 아직 안 왔는데?”
“그럼 일단 기다렸다가 순서대로 맞춰야겠다.”
이때 필요한 것이 순서 번호다.
TCP는 각 데이터 조각에 번호를 붙여두기 때문에, 받는 쪽은 도착한 데이터들을 순서대로 다시 맞출 수 있다.
택배로 비유하면 여러 박스에 나누어 물건을 보낼 때,
각 박스에 “1번 박스, 2번 박스, 3번 박스”라고 적어두는 것과 비슷하다.
박스가 순서대로 도착하지 않아도 번호를 보면 다시 정리할 수 있다.
TCP에서는 데이터를 받은 쪽이 그냥 조용히 있지 않는다.
잘 받았으면 “여기까지 받았다”는 응답을 보내준다.
이 응답이 ACK다.
ACK는 확인 응답이라고 생각하면 된다.
보낸 쪽 입장에서는 ACK를 받아야 안심할 수 있다.
예를 들어 데이터를 보낸 쪽이 이렇게 생각한다고 해보자.
“100번부터 데이터를 보냈다.”
“상대가 150번까지 잘 받았다고 알려줬다.”
“그럼 이제 151번부터 보내면 되겠네.”
실제 TCP의 ACK 번호는 보통 “다음에 기대하는 번호”를 의미한다.
즉, 받는 쪽이 ACK 151을 보냈다면 “150까지는 받았고, 다음은 151부터 보내줘”라는 의미로 볼 수 있다.
여기서 TCP가 판단하는 방식은 꽤 단순하다.
“내가 보낸 데이터에 대한 ACK가 왔는가?”
“왔다면 그 데이터는 도착한 것으로 본다.”
“오지 않았다면 중간에 문제가 생겼을 가능성이 있다.”
이런 식으로 TCP는 데이터를 하나하나 추적한다.
네트워크에서는 데이터가 중간에 사라질 수 있다.
케이블이 끊어진 것처럼 극단적인 상황이 아니어도, 혼잡하거나 오류가 발생하면 패킷이 유실될 수 있다.
TCP는 데이터를 보낸 뒤 일정 시간 동안 ACK를 기다린다.
그런데 ACK가 오지 않으면 이렇게 판단한다.
“상대가 데이터를 못 받았나?”
“아니면 ACK가 돌아오는 길에 사라졌나?”
“어쨌든 확인이 안 됐으니 다시 보내야겠다.”
이것이 재전송이다.
여기서 중요한 건 TCP가 데이터 유실을 완전히 막는 것은 아니라는 점이다.
대신 유실이 발생했을 때 이를 감지하고 복구하려고 한다.
즉, TCP의 신뢰성은
“절대 안 잃어버린다”가 아니라
“잃어버렸을 가능성이 있으면 다시 보내서 맞춘다”에 가깝다.
TCP는 데이터가 도착했다고 해서 무조건 믿지 않는다.
전송 과정에서 데이터가 손상될 수도 있기 때문이다.
그래서 TCP는 데이터에 오류가 있는지 확인하고, 문제가 있다고 판단되면 해당 데이터를 정상적으로 받은 것으로 처리하지 않는다.
그러면 결국 ACK가 제대로 가지 않거나, 필요한 데이터가 다시 전송된다.
이 흐름을 오류 제어라고 볼 수 있다.
정리하면 오류 제어는 이런 질문에 대한 TCP의 대응이다.
“데이터가 중간에 사라지지 않았는가?”
“데이터가 깨지지 않았는가?”
“순서가 어긋나지는 않았는가?”
“문제가 있다면 다시 받을 수 있는가?”
TCP는 순서 번호, ACK, 재전송을 이용해서 이런 문제를 해결하려고 한다.
데이터를 정확히 보내는 것도 중요하지만, 속도 조절도 중요하다.
보내는 쪽이 너무 빠르게 데이터를 밀어 넣으면 받는 쪽이 감당하지 못할 수 있다.
예를 들어 서버 성능은 좋은데 클라이언트의 처리 속도가 느리다면, 클라이언트는 받은 데이터를 버퍼에 쌓아두다가 결국 넘칠 수 있다.
이 문제를 해결하기 위한 것이 흐름 제어다.
흐름 제어는 간단히 말하면
받는 쪽이 감당할 수 있는 만큼만 보내도록 조절하는 것이다.
받는 쪽은 자신이 받을 수 있는 여유 공간을 알려준다.
보내는 쪽은 그 정보를 보고 전송량을 조절한다.
비유하면 물을 컵에 따르는 것과 비슷하다.
물을 따르는 사람이 아무리 빠르게 따를 수 있어도, 컵이 작으면 넘친다.
그러면 컵의 크기에 맞춰 천천히 따라야 한다.
TCP도 마찬가지다.
“상대 버퍼에 여유가 있나?”
“있다면 더 보내도 되겠다.”
“여유가 적다면 잠깐 줄여야겠다.”
이런 식으로 받는 쪽의 상태를 고려한다.
흐름 제어가 받는 쪽의 상태를 보는 것이라면,
혼잡 제어는 네트워크 전체의 상태를 보는 것이다.
받는 쪽 컴퓨터가 아무리 충분히 받을 수 있어도, 중간 네트워크가 혼잡하면 문제가 생긴다.
라우터나 링크에 트래픽이 몰리면 패킷이 지연되거나 유실될 수 있다.
이때 TCP는 이렇게 판단한다.
“ACK가 늦게 오네?”
“패킷이 유실된 것 같네?”
“네트워크가 혼잡한가 보다.”
“그럼 보내는 속도를 줄이자.”
혼잡 제어는
네트워크가 감당할 수 있는 만큼만 보내도록 조절하는 것이다.
흐름 제어와 혼잡 제어는 비슷해 보이지만 기준이 다르다.
흐름 제어는 받는 쪽을 보호한다.
혼잡 제어는 네트워크를 보호한다.
여기서 중요한 건 TCP가 자기 마음대로 최대 속도로 보내지 않는다는 점이다.
상대방의 처리 능력과 네트워크 상황을 보면서 전송량을 조절한다.
만약 TCP가 데이터를 하나 보내고, ACK가 올 때까지 가만히 기다린다면 어떻게 될까?
신뢰성은 있을 수 있지만 너무 느리다.
특히 거리가 먼 서버와 통신할 때는 ACK가 돌아오는 시간만큼 계속 기다려야 한다.
그래서 TCP는 여러 데이터를 연속으로 보낼 수 있게 한다.
이때 사용하는 방식이 슬라이딩 윈도우다.
슬라이딩 윈도우는 말 그대로 “보낼 수 있는 데이터의 범위”를 창문처럼 정해두는 방식이다.
예를 들어 현재 1번부터 5번까지 보낼 수 있다고 해보자.
보내는 쪽은 ACK를 기다리지 않고 1, 2, 3, 4, 5번 데이터를 연속으로 보낼 수 있다.
이후 받는 쪽에서 “1번 잘 받았고 다음은 2번부터 필요해”라고 알려주면,
창문이 한 칸 옆으로 이동한다.
그러면 이제 6번 데이터를 추가로 보낼 수 있다.
이렇게 윈도우가 옆으로 밀리듯 이동하기 때문에 슬라이딩 윈도우라고 부른다.
이 방식 덕분에 TCP는 매번 하나씩 멈춰서 확인하지 않고도,
여러 데이터를 효율적으로 보내면서 신뢰성을 유지할 수 있다.
TCP의 신뢰성은 하나의 기능으로 만들어지는 것이 아니다.
순서 번호는 데이터의 순서를 맞추기 위해 필요하다.
ACK는 데이터가 잘 도착했는지 확인하기 위해 필요하다.
재전송은 유실된 데이터를 다시 보내기 위해 필요하다.
오류 제어는 잘못된 데이터를 걸러내기 위해 필요하다.
흐름 제어는 받는 쪽이 감당 가능한 만큼 보내기 위해 필요하다.
혼잡 제어는 네트워크가 감당 가능한 만큼 보내기 위해 필요하다.
슬라이딩 윈도우는 효율적으로 여러 데이터를 보내기 위해 필요하다.
결국 TCP는 이런 식으로 움직인다고 볼 수 있다.
“이 데이터는 몇 번째 데이터인가?”
“상대가 여기까지 받았다고 했는가?”
“ACK가 안 왔으면 다시 보내야 하는가?”
“받는 쪽이 더 받을 수 있는가?”
“네트워크가 지금 버틸 수 있는가?”
“얼마나 한 번에 보내도 되는가?”
TCP는 계속 이런 판단을 하면서 데이터를 보낸다.
그래서 TCP는 UDP보다 단순히 “느리다”라고만 볼 수는 없다.
TCP는 더 많은 확인과 조절을 하면서 안정적인 전송을 목표로 한다.
웹 페이지를 불러오거나, 파일을 다운로드하거나, 로그인 요청을 보내는 상황에서는 데이터가 빠지는 것보다 정확히 도착하는 것이 더 중요하다.
이런 상황에서 TCP의 신뢰성은 꽤 큰 의미를 가진다.
결국 TCP는 네트워크 위에서 데이터를 보내는 동안 계속 확인한다.
보냈는지, 도착했는지, 순서가 맞는지, 다시 보내야 하는지, 너무 많이 보내고 있지는 않은지.
이런 과정을 통해 TCP는 불안정할 수 있는 네트워크 위에서 최대한 안정적인 통신을 만들어낸다.