글 작성자: Sowhat_93

TCP 는 원격의 Ack 통지로부터  내가 보낸 데이터가 잘갔는지 확인하고,

Seq 통지를 통해 보낼 데이터의 순서를 알린다.

 

1. Seq로 상대에게 데이터 순서를 통지하고,

2. 상대로부터 Ack를 받은뒤,

3. 이에 따라 Seq를 정하고 다시 송신.

 

한번보내면, 상대도 응답을 한번 보내고 그걸 받아야만 다음 차례 송신을 진행하는 이런 방식을 핑퐁 방식 이라고 한다.

핑퐁 방식은 무엇보다도 상대로부터 Ack를 받고 다시 보내기 때문에,

상대가 어디까지 받았는지, 내가 어디서부터 보내야 할지가 매우 명확하며, 방식이 매우 심플하다.

허나 , 데이터 송신 후 상대측으로부터의 Ack 통지를 기다릴때, 상대측의 응답이 늦어질 경우

상당한 손해이다. 보낼 것은 많은데, 상대측으로부터의 응답이 없다는 이유로 기다릴 수 밖에 없다.

 

그래서 TCP는 윈도우 제어 방식을 사용한다.

Ack를 기다리지 않고, 보내고 싶은 만큼 왕창 보낸다.

어짜피 Ack는 언젠가 온다. 그때 가서 상대가 못받았다고 하면, 그때 다시 주지 뭐.

한다는 거다.

 

괜찮아 보인다.  핑퐁 처럼 Ack 기다리느니 남는시간에 안놀고 열심히 보내는 거니까.

그런데 고려해야할 것이 남아있다.

송신지측에서 보낼게 많아서, 당장 왕창 보냈다.

그런데 상대측에서 받으려고 보니 Receive Buffer 사이즈가 턱없이 작다.

2000을 보냈는데 500밖에 못받았다.

그러면 나는 그것부터 다시 보내야 한다. 

보낸건 2000인데, 1500을 못받았다는 것을 Ack를 받고 알게되었다.

결국 핑퐁이랑 똑같다.

이런 삽질을 막기 위해서,  Window Size를 정한다.

 

 

상대측의 Window Size를 알아내 적당하게 보내는 것이 오히려 낫다.

 

Window Size란 무엇인가? 

아주 간단하고도 당연한 개념이다.

상대의 TCP 스택의 수신 버퍼가 최대한 받을 수 있는 만큼만 보내려고 한다.

그 정도가 얼마냐? 이다.

 

송신측은 큰 버퍼를 두고, 상대 윈도우 사이즈에 맞게 보낸다.

상대로부터 잘 받았다는 Ack를 받으면 다시 보낼 필요 없으므로 그만큼을 송신 버퍼에서 꺼낸다.  

 

수신측은 큰 버퍼를 두고, 받은 데이터에 대해 Ack를 보낸다.

또한 App에서 TCP 스택 버퍼에서 데이터를 빼가면,  Window Size가 변했으므로

이 상황을 상대측에 통보한다.

 

위 상황이 마치 양쪽이 미닫이 창문을 밀고 당기는 것과 비슷하게 생겼다고 해서,

Sliding Window 라는 이름이 붙었다.

 

 

TCP 헤더 안에 Window Size 통지를 위한 부분이 있다.

 

Window Size를 통지받은 측에서는, '아 상대가 딱 이 만큼까지만 받을 수 있구나.'

인지하고 아무리 많아도 딱 그거 까지만 보내도록 한다.

수신측은 데이터를 받고, Window Size를 줄인다.

 

Window Size가 줄어나가는 것은 매우 당연하고 심플한 일이다.

그러면 언제 Window Size가 늘어나고, 언제 통보를 할까?

 

App에서 recv등을 호출해 TCP 스택의 수신버퍼에서 데이터를 꺼내가면,

TCP스택의 입장에서는 App으로 전달했으니 끝.

꺼내간 만큼 공간이 났으므로 Window Size를 늘린다.

 

App에서 recv등으로 TCP 스택으로부터 데이터를

빼가면, 수신 윈도우가 갱신되었음을 송신측으로 알린다.

 

 

허나 recv를 호출할때마다 매번 빈번하게 알릴 필요는 없다.

수신측에서의 Ack의 통지 타이밍은 데이터를 받자마자 바로 진행된다.

이때 같이 Window Size를 같이 알리면 된다.

 

더군다나 송신측도 수신측으로부터 오는 Ack를 계산해서 Windows Size를 계산한다.

따라서 넘치게 보낼 일 ? 없다.

 

헌데, 가끔 수신측 윈도우 업데이트 잠시 늦어지는 경우가 생기는데,

Window Size가 0이 된 경우다. 

그러면 이때에는 App에서 꺼내간만큼 Windows Size가 늘어났으니 보내는가?

그것은 또 아니다.

수신측의 Window Size가 30만 byte 정도 라고 가정하자.

지금은 Window 가 가득찬 상태다.

수신측 App에서 recv를 통해 TCP 스택의 데이터를 10byte 씩 빼가고 있다.

 

그럼 10byte 씩 빠졌다고 매번 알릴까? 알려봤자 더 혼잡해진다.

커다란 물탱크 에서 종이컵으로 물을 빼내고 있는 상황을 가정하자.

종이컵 한컵씩 물을 퍼내고 있다고 송신측에게 알리면 송신측은 또 종이컵 한컵을 준다.나는 종이컵 한컵을 잘 받았다고 송신측에게 알린다. 

 

이것 자체가 부하가 걸리는 일이다. 그래서 커다란 물탱크에서 전부다 퍼낼때까지 기다린다.

아래의 그림을 보자.

송신측에서는 수신측의 App 이 보낸 데이터에 대한 처리가 오래 걸릴 것으로 예상하고, 

우선은 송신을 중단하고 , Window Full , ZeroWindowProbe , ZeroWindow 를 주고 받으며

일정 시간 마다 수신측이 데이터를 받을 준비가 되었는지 검사한다.