네트워크

개발자가 TCP 송/수신 원리를 알아야 하는 이유

25G 2023. 8. 20. 21:12

시나리오

server쪽에서 file하나를 들고 있다가 클라이언트에서 사용자가 파일을 다운로드받는 상황

서버입장

Server프로그램이 돌아가고있고 socket인터페이스를 클라이언트와 통신하기위해 열어놨을것입니다. 소켓통신은 다음과 같은 동작을 하게 돼 있습니다.
Recive : 읽기
Write(send) : 보내기

그럼 서버 컴퓨터 저장소와 서버프로그램이 통신을 하게 될텐데 이 IO가 일어날때 메모리에 할당이 일어나는데
이때 데이터가 일정수준이상 커지면 데이터를 끊어서(대부분의 데이터는 끊어짐) 이동하는데 이때 socket과 tcp사이에 일어나는 IO를 Buffered IO 라고 합니다.
자, 이제 이 buffer가 ip 패킷이 있는 계층으로 내려가면서 세그먼트단위로 다시 한번더 쪼개집니다. 이 tcp계층의 버퍼가 ip계층의 세그먼트단위로 쪼개질때 새그먼트마다 각자의 숫자(식별숫자)가 붙여집니다. 그렇게 패킷이 만들어지고 그 패킷이 L2레벨로 내려가면 Frame(프레임)으로 인캡슐레이션됩니다. 이러한 과정을 거쳐서 서버컴퓨터를 빠져나와 인터넷을 거치고 클라이언트 컴퓨터로 전송됩니다.

클라이언트 입장

서버가 보낸 패킷이 NIC을 보고 클라이언트 컴퓨터를 찾아갔습니다. 그리고 패킷이 NIC를 타고 Driver를 거치면서 디캡슐레이션과정이 일어난다. 그럼 프레임이 벗겨지면서 안에 있는 패킷이 보이게 되는데 이 패킷에 붙어있는 식별숫자를 보고 정렬을 한후 다음 받아야될 데이터를 잘받았다는 신호를 서버컴퓨터에게 다시한번 보낸다. 이를 ACK라고 합니다. 이러한 과정이 클라이언트컴퓨터에서 일어나고 있는동안 서버는 버퍼를 열심히 보내고 있는것이 아니라 다음 보낼 패킷을 준비시켜 놓고서는 클라이언트의 ACK를 기다리고 있습니다. 즉 클라이언트에서 ACK를 보내기전까지 서버에서 데이터를 준비시키고 오메불망기다리고 있는 것입니다. 이 과정 때문에 신뢰성있는 통신은 보장되지만 지연이 일어나서 속도가 느려지게 됩니다.

여기서 중요한 점은 클라이언트에서 ACK를 보낼때 ACK에 windowSize를 포함하여 전송합니다. 그리고 서버쪽에서 클라이언트의 ACK가 왔을때 windowSize를 보고 다음패킷을 전송을 할지 말지 결정합니다. 만약 windowSize가 보낼 양보다 적으면 서버쪽에서 더이상 패킷을 보내지않습니다. 즉 수신측의 windowSize > mss(최대 세그먼트사이즈)을 따지는 것입니다. 이 조건식을 따졌을때 true이면 패킷을 전송하고 false이면 wait가 걸립니다.

다시 클라이언트의 입장으로 와서 클라이언트 컴퓨터는 TCP를 통해서 OS가 열심히 소캣으로 받은 패킷들을 조립해서 열심히 읽어드리고 있습니다(read). 이때 만약 읽는 속도가 network 수신 속도보다 느리다면 버퍼의 여유가 점점 사라지고 windowsize는 점점 작아집니다.(위에서 windowSize가 작아지면 패킷을 wait을 시킨다고 했던것과 맞물리는 부분입니다.)

이를 개발자 입장에서 생각한다면 개발자가 프로그램을 잘못 개발해서 버퍼를 읽는 속도가 많이 느리다면 network 에서 장애요인을 찾을 것이 아니라 TCP Buffer를 소켓단위로 올리는 작업에대한 속도를 채크하고찾아볼 줄 알아야한다는 점 입니다.

결론

서버와 클라이언트가 통신을 할때 수신이 느린것은 결과적 현상이고 그 원인은 송신이 느린것때문인데 송신이 느린것은 무조건 서버가 느려서 그런것이 아니라 클라이언트가 데이터를 받을 준비가 늦어서 안될 수도 있다라는것을 항상 염두를 해 두어야 한다. 이를 구별해 내는것이 정말 중요한 부분이라고 할 수 있습니다.