먼저 하나의 상황을 가정해보자.
파리에서 에펠탑을 택배로 보내려면 어떻게 해야 할까?
1.
첫 번째로 에펠탑이 너무 크기 때문에 분해하여 크기를 줄이자.
택배라는 형태로 운송이 가능한 형태까지 줄여야한다. 즉, MTU 이하로 줄여야한다.
2.
두 번째로는 운송을 하고 마지막으로는 조립을 하면 된다.
분해는 송신측에서 하는 것이고,
조립은 수신측에서 하게 된다.
데이터는 packet이라는 단위로 데이터가 유통된다.
이 상황을 큰 틀로 이해하고 TCP/IP의 송수신 흐름에 대해서 알게되면 이해가 보다 빠를 것이다.
전체적인 흐름에 대해 이야기하기 전, 몇 가지 개념을 간략히 정리하고 넘어가겠습니다. 이 개념을 숙지하면 플로우를 이해하는 데 도움이 됩니다. TCP는 명확하지 않아도 괜찮습니다. 전체적인 흐름을 이해한 후 TCP를 공부하면 더 빠르게 학습할 수 있습니다.
•
TCP: 연결 지향. 연결이 정상적으로 완료된 후 송수신이 이루어짐. 순서가 중요하다.
•
Socket에서는 I/O(입출력 활동)가 일어난다.
I/O가 일어날 때 이 socket에 attached된 메모리 공간인 buffer가 존재한다.
이 buffer가 있으면 Buffered I/O가 되고, buffer가 없으면 NonBuffered I/O가 되는 것이다.
송수신 구조는 다음과 같이 크게 송신, 유통, 수신의 세가지로 분류할 수 있다.
만약 내가 네이버에서 1.4MB 크기의 어떤 자료를 다운로드 받으려고 한다.
MTU의 최대 단위는 약 1.4KB이기 때문에 약 1000배가 차이가 난다.
따라서 한번에 이 자료를 다운로드 받을 수 없고, 잘라서 보내야 한다
1,2,3,4,5,6,7,8,9,10의 순서로 총 10개로 분할되었고 이 자료를 순차적으로 보내야한다.
Send(송신)
송신과정에서는 위의 데이터를 먼저 3개만 보낸다고 가정하자.
그렇다면 먼저 이 3개의 데이터를 Process Buffer에 올려놓는다.
그리고 이를 송신 측 Socket Buffer memory로 옮기는데 이 과정을 send(write)라고 한다.
이때 이 3개의 데이터의 단위는 stream이 된다.
이제 Socket Buffer의 데이터가 L4 → L3 → L2 → L1 으로 계층을 타고 내려가는데, L4 단계의 TCP를 만나는 지점에서 Segmentation(분해)를 한다. 바로 메모리에 올라가있는 1,2,3의 데이터 중 1 을 먼저 분할하여 보낸다는 것이다. 이 1이라는 데이터는 이제 L4 계층에서 Segment라는 단위가 된다.
이후 L3 계층으로 내려가면 EnCapsulation이 진행되면서 데이터를 포장한다. 포장된 데이터가 바로 Packet이다.
이 Packet이 L2 계층으로 내려가서 다시 한번 Encapsulation이 되며 L2 Frame이 된다.
이 상태에서 Routing을 하는 등의 유통과정으로 나가는 것이다.
요약: 일정 수준의 블록 데이터를 읽어 프로세스 내부의 버퍼 메모리에 담은 후 송수신하는 I/O 버퍼에 담고난 후 계층을 타고 내려가면서 캡슐화가 진행된다.
Network(유통)
유통 과정은 택배라고 생각하면 이해가 쉽다. 우리가 현재 유통해야하는 택배는 L2 Frame이라는 택배 박스이다. 우리가 택배를 보낼 때, 택배 기사님께서 트럭에 우리가 보내려는 택배를 싣는다. 이때 트럭이 바로 목적지로 향하는가? 그렇지 않다. 택배사의 물류 분류 체계에 따라 물류센터 혹은 허브로 간 후 목적지의 담당 물류센터로 옮겨진 후 담당 택배 기사님이 물건을 적재하시고 다른 기사님이 다른 트럭으로 목적지에 택배를 가져다 주신다.트럭이 바로 Frame이라고 생각하자.
우리가 처음 택배를 보낼 때 물건을 실었던 그 트럭이 계속 사용되는가? 아니다. 트럭은 계속 바뀐다. Frame도 마찬가지다. Network 과정에서 Routing 등의 과정을 거치는 동안 Frame 자체는 변할 수 있다. 이렇게 Frame을 목적지인 수신측에 가져다주면 유통 과정은 끝이 난다.
요약: 프레임 형태로 데이터가 이동한다.
Recieve(수신)
조립은 분해의 역순이고 수신은 송신의 역순이다.
따라서 수신 측에서 Frame을 전달 받은 후 계층을 위로 타고 올라갑니다. L2 계층에서 Frame을 받고, L3 계층에서 Decapsulation이 발생하여 L3 Packet이 생성됩니다. 이후 Packet은 L4 계층에서 다시 Decapsulation되어 Segment가 되며, 이 Segment는 "1"이라는 데이터를 수신 측의 소켓 버퍼 메모리에 "채우게 됩니다."
(이 채우는 활동 자체는 운영 체제(Kernel mode)의 TCP가 담당합니다.)
이후 수신측은 소켓 버퍼 메모리에 올려진 데이터를 receive(read)하여 프로세스 처리 버퍼 메모리로 가져옵니다. 그리고 수신측의 처리 버퍼는 메모리를 '비웁니다.' 이러한 채우고 비우기의 과정은 톱니바퀴처럼 끊임없이 반복됩니다.
요약: 반캡슐화를 먼저 진행하고, 세그먼트 하나를 끄집어내서 수신측 입출력 버퍼에 담고, 애플리케이션이 퍼가서 비워낸 후 처리로 가는 과정이다.
Acknowledgement
데이터가 항상 모든 경우에 위의 과정을 정상적으로 수행하면 다행이지만 절대 그렇지는 않다. 송신측에서는 수신측이 데이터를 제대로 받았는지 확인을 하고 싶다라는 니즈가 있다. 위의 간략한 개념 정리에서 TCP는 연결 지향이며, 순서가 중요하다고 했다. 따라서 TCP는 데이터를 받으면 데이터를 잘 받았는지, 받지 못 했는지에 대한 피드백을 제공하는데 이 피드백을 ACKnowledgement라고 한다.
만약 데이터가 1,2,3,4,5의 데이터 중 1, 2까지 정상적으로 수신이 완료됐다면 TCP에서 피드백으로 ACK#3 + (여유 공간)과 같이 피드백을 주는데, 이 뜻이 2번까지 잘 받았고 3번이 올 차례이다. 라는 의미로 해석하면 된다. (여유 공간도 같이 고려한다)
이 때, 송신측에서는 wait for ACK#3 처럼 ACK#3의 응답을 기다리는 wait time이 존재한다. 기다리는 도중에 수신측에서 ACK#3을 보냈으니 이를 송신측에서 받고 정상적으로 데이터가 송신되었음을 확인하고 3번 데이터부터 다시 보내는 것이다. 참고로 여유 공간이 존재하지 않는다면 추가적으로 데이터를 보내지 않는다.
참고로 UDP 소켓 같은 경우 데이터(데이터그램)이 최소 1개 도착해 있으면 즉시 리턴하고 그렇지 않으면 데이터가 1개 도착할 때까지 블로킹이 된다. 수신 함수가 수신 버퍼에서 데이터를 꺼내는 속도가 운영체제가 수신 버퍼의 데이터를 채우는 속도보다 느리면 데이터(데이터그램)이 1개 도착할 때까지 블로킹이 된다.
수신 함수가 수신 버퍼에서 데이터를 꺼내는 속도가 운영체제가 수신 버퍼의 데이터를 채우는 속도보다 느리면 네트워크 선로로 UDP 데이터그램 A가 도착했지만, UDP 소켓 안의 수신 버퍼가 데이터그램 A를 담을 여유 공간이 없으면 데이터그램 A는 그냥 버려진다.
이때 송신 함수 sendTo()의 블로킹은 발생하지 않는 것이 TCP와 차이점 이다.
대표적인 Network 장애 유형
Network는 생각보다 굉장히 불안정하기 때문에 여러 장애가 나타날 수 있다.
•
Loss (유실)
◦
Lost Segment
◦
데이터 송신 과정에서 데이터가 유실 된 경우이다. 유실된 데이터의 행방은 묘연해진다..
◦
원인: Network
•
Re-transmission + ACK Duplicate
◦
데이터를 보냈는데 그에 대한 응답이 계속 없어서 다시 보내는 것.
하지만 아주 간발의 시간차이로 수신측에서 ACK를 보냈는데, 그와 동시에 송신측에서 기다리다가 또 보내버려서 발생하는 장애.
이 현상이 발생하면 송신 측에서는 1, 2의 데이터를 다시 보내게 되고 수신 측에서는 ACK#3을 보냈는데 다시 1, 2 데이터를 주네..? 하면서 일단 데이터를 받고.. 하는 현상이 발생한다. 이러면 이제 ACK Duplicate가 발생한다.
◦
원인: Network 문제 or End-point간의 합 문제
•
Out of order
◦
1, 2, 3, 4의 순서가 아닌 1, 2, 4, 3 혹은 1, 2, 4의 순서로 데이터가 오는 일이 발생한다. 이 경우는 TCP 수준에서 일정한 수준의 보정 작업 진행한다.
◦
원인: Network
•
Zero window
◦
수신 socket buffer의 여유공간의 메모리 크기를 window size 라고 부른다. 네트워크는 안정적으로 송수신을 완료했는데, Process 단에서 어떠한 이유로 buffer를 비우지 않는 것이다. 비우지 않으니 Socket buffer가 full이 되어 여유 공간이 존재하지 않는 현상이다.
네트워크의 송수신 속도가 데이터를 처리하는 속도보다 빠를 경우 발생할 수 있다.
◦
원인: End-point 단계의 Application(프로그램)에서 원인을 찾아 해결해주어야 한다.