Skip to content

패킷 여행 in Data Stream

ozt88 edited this page Apr 13, 2015 · 1 revision

패킷의 데이터 flow

네트워크 레이어에서 패킷의 흐름을 알아보았다. 이제 패킷을 포함한 데이터가 어떤 방식으로 전달되는지 구체적으로 알아보자

우선 write 시스템 콜을 한 순간 유저가 할당한 데이터가 한번에 프로토콜 스택(운영체제에서 관리하는 여러 프로토콜들의 통칭)의 버퍼로 복사되는 것이 아니라는 것을 알아야한다. non-blocking I/O를 호출해보면 전송하고 싶은 데이터의 크기와 리턴받은 전송량이 차이가 난다. 개략적인 이유는 이전의 LINGER 옵션파트와 Nagle 옵션 파트에서 살짝 이야기한 바 있는데, 어쨋거나 전송에 사용되는 Send Buffer는 받자마자 모든 데이터를 전송할수가 없다. 자세한 이유는 앞으로 차차 알아갈 것이므로 좀만 기다려달라. 어쨋든 중요한 것은 Send Buffer는 제한된 메모리를 갖고 있으며, 항상 완전히 비어있는 것은 아니다. 그러니까 우리가 처음 write 콜을 하면 Send 버퍼가 수용할 수 있는(비어있는) 양만큼만 데이터가 전달된다.

일단 첫번째 이유는 네트워크 효율을 좋게만들기 위해서 프로토콜이 최대한 데이터를 모아서 보내려고 하기 때문이다. Nagle 옵션의 내용을 생각해보면 이해하기 쉽다. 그러면 운영체제는 얼마만큼 데이터를 모아서 언제 데이터를 보내는 걸까? 전송하는 조건 또는 기준이 있을 것이다. 일단 알기 쉬운 기준은 전송 가능한 최대크기이다. 아무리 데이터를 많이 모으려해도 Ethernet 수준에서 결정한 최대 데이터 크기 MTU(Maximum Transmission Unit)를 넘을 수 없다. MTU에서 프로토콜 헤더들을 제외한 사이즈를 MSS(Maximum Segment Size)라고한다. 그러니까 데이터를 모아봤자 MSS가 넘으면 어차피 나눠서 보내야한다. 그러니까 모인 데이터 크기가 MSS를 초과하거나 육박하게되면 그때 데이터를 전송하는 방식이 있다. 또 다른 기준은 타이밍이다. 앞에서 말한 것처럼 데이터를 꽉꽉 눌러담아서 보내는 경우, 보내야되는 소량의 데이터가 있는데 오랜 시간동안 후속 데이터가 없는 상황이 있을 수 있다. 그러면 반응성이 안좋아도 너무 않좋다. 그러니까 타이머로 정해진 시간마다 패킷을 전달하는 것이다. 데이터 크기를 기준으로 하면 효율이 좋아지고, 타이밍을 기준으로 하면 반응성이 좋아진다. 똑똑한 OS 개발자님들이 둘을 잘 섞어서 동작을 정의했을 것이다. 그래도 유저가 원하는 결과가 나오지 않는다면, 그때 Nagle옵션을 사용하여 직접 개입하는 것이다.

두 번째 이유는 한번 보냈다고 해서 상대방이 받을 거란 보장이 없기 때문이다. 네트워크의 세상은 혼돈의 카오스다. 보낸 패킷이 사라지는 것은 일상 다반사이다. 그러니까 TCP는 안전한 전송을 보장하기 위해 패킷이 도착했는지를 ACK번호를 사용하여 체크하고, 오래도록 ACK가 오지 않으면 다시 재전송을 해야한다. 데이터가 쪼개져서 보내지기 때문에, TCP 헤더에 보내는 데이터의 시퀀스 번호를 매겨서 받은 상대방이 보낸 데이터의 전송 상태를 확인할 수 있게한다. 데이터를 받은 상대방은 시퀀스 번호를 체크해서 이제 어디서부터 받아야한다는 것을 알 수 있다. 이 정보를 헤더에 담긴 ACK번호를 통해 수신응답겸 전달한다. 시퀀스 번호는 1부터 시작하면 악의적인 접근이 가능하므로 난수를 생성하여 거기서부터 출발한다. 그리고 수신측도 보통 ACK만 보내지 않고 다음번 응답까지 합쳐서 piggy backing 하는데 아래의 그림만 봐도 쉽게 이해가능하니 더이상의 설명은 생략한다.

이런 방식으로 송 수신을 진행하기 때문에 전송했다고 해서 Send 버퍼에 있는 데이터를 함부로 비워버릴 수 없다. 재전송을 고려해서 확실히 전달받았는지 ACK값을 통해 체크한 뒤에야 비울 수 있다.