역다중화
고친 사람 github-actions[bot]
역다중화는 한 통로로 섞여 들어온 데이터를 원래 받을 곳에 나눠 줍니다. 받는 쪽은 데이터에 붙어 온 번호를 읽고 그 번호가 가리키는 곳으로 넘깁니다. 보내는 쪽이 여러 흐름을 한 통로에 싣는 다중화와 늘 짝을 이룹니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 섞여 도착한 데이터를 주인별로 갈라 주는 일입니다. 서버 한 대에서 웹 서버와 데이터베이스가 같이 돌아도 각자 제 데이터만 받는 것이 이 덕분입니다.
왜 이렇게 하나 — 선 하나로 여러 프로그램이 통신하면 도착한 데이터가 전부 한 입구로 들어옵니다. 누구 것인지 가려 주는 단계가 없으면 받는 쪽은 뒤섞인 바이트 더미만 쥐게 됩니다. 거꾸로 프로그램 하나가 선을 혼자 쓰면 가를 것이 없으니 이 단계도 필요 없습니다.
어떻게 도나
- 데이터가 도착하면 맨 앞에 붙은 안내문을 읽습니다
- 안내문에 적힌 번호로 다음에 넘길 곳을 고릅니다
- 안내문을 떼고 남은 것을 그곳에 넘깁니다. 넘겨받은 쪽도 같은 일을 되풀이합니다
대가 — 번호 하나에는 주인도 하나여야 합니다. 두 프로그램이 같은 번호를 달라고 하면 뒤에 온 쪽이 거절당합니다. 번호가 가리키는 주인이 없으면 데이터는 버려집니다.
상세
아파트 관리실에 택배 상자가 한 트럭 도착합니다. 관리실 직원은 상자마다 붙은 송장에서 동과 호수를 읽고 집집마다 나눠 둡니다. 트럭은 한 대였지만 상자는 저마다 제 집을 찾아갑니다.
다중화의 반대편
한 통로를 여러 흐름이 나눠 쓰는 것을 다중화라고 부릅니다. 흐름은 서로 상관없는 데이터 줄기 하나입니다. 브라우저가 웹 페이지를 받는 일이 흐름 하나입니다. 같은 컴퓨터의 메일 프로그램이 메일을 받는 일이 또 하나입니다.
역다중화는 받는 쪽에서 이 흐름들을 도로 갈라내는 일입니다. 영어로는 demultiplexing 입니다. 줄여서 demux 라고도 씁니다.
데이터는 한 번에 통째로 가지 않고 작은 덩어리 여럿으로 나뉘어 갑니다. 이 문서는 그 덩어리 하나하나를 조각이라고 부릅니다. 여러 흐름의 조각이 한 통로를 번갈아 지나니 받는 쪽에는 섞인 채로 도착합니다. 역다중화는 조각을 하나씩 보고 어느 흐름의 것인지 가려 주인에게 넘깁니다.
이 단계가 없으면 받는 쪽에는 뒤섞인 바이트 더미만 남습니다. 브라우저 몫과 메일 프로그램 몫이 한데 엉겨 어느 쪽도 제 데이터를 못 씁니다. 거꾸로 통로를 흐름 하나가 혼자 쓰면 가를 것이 없으니 역다중화도 필요 없습니다.
가려내려면 조각마다 표시가 있어야 합니다. 보내는 쪽은 다중화할 때 조각마다 어느 흐름의 것인지 알리는 번호를 붙입니다. 이 번호를 식별자라고 부릅니다. 역다중화는 이 식별자를 읽는 쪽의 일입니다.
식별자는 헤더에 적힙니다. 헤더는 데이터 앞에 붙어서 받는 쪽에게 이 데이터를 어떻게 다룰지 알리는 안내입니다. 받는 쪽은 본문을 열기 전에 헤더부터 읽습니다.
계층마다 한 번씩 가른다
이 소절은 도착한 조각 하나가 컴퓨터 안에서 제 프로그램까지 가는 길을 따라갑니다. 갈림길이 한 번이 아니라 여러 번이라는 것을 보입니다.
네트워크 통신은 일을 계층으로 나눠 맡깁니다. 선 위로 데이터를 나르는 계층, 주소를 보고 목적지 컴퓨터를 찾아가는 계층, 프로그램끼리 데이터를 주고받게 하는 계층이 아래에서 위로 쌓여 있습니다. 각 계층은 맡은 일만 하므로 한 계층을 바꿔도 다른 계층은 그대로 둘 수 있습니다.
보내는 쪽에서는 계층마다 자기 헤더를 하나씩 덧붙입니다. 이것을 캡슐화라고 부릅니다. 각 헤더에는 받는 쪽의 같은 계층에게 전할 말이 적힙니다.
받는 쪽은 거꾸로 아래 계층부터 자기 헤더를 읽고 떼어 낸 뒤 남은 조각을 위로 올립니다. 헤더를 떼는 일은 역캡슐화입니다. 위의 누구에게 올릴지 고르는 일이 역다중화입니다. 그래서 역다중화는 계층마다 한 번씩 일어납니다.
맨 아래 계층에서는 이 조각을 프레임이라고 부릅니다. 프레임 헤더에는 안에 든 것이 무슨 종류인지 적는 칸이 있습니다. 컴퓨터는 이 칸을 보고 조각을 넘길 처리기를 고릅니다. 이것이 첫 번째 갈림길입니다.
계층마다 따르는 통신 규칙을 프로토콜이라고 부릅니다. 보내는 쪽과 받는 쪽이 함께 지키는 통신 약속입니다. 양쪽이 같은 프로토콜을 따라야 상대가 붙인 헤더를 읽을 수 있습니다.
프레임 안의 조각을 넘겨받는 쪽은 대개 IP(Internet Protocol, 인터넷 프로토콜)입니다. IP 는 주소를 보고 데이터를 목적지 컴퓨터까지 나르는 프로토콜입니다.
IP 헤더에는 프로토콜 번호 칸이 있습니다. 이 칸은 안에 든 조각을 위쪽의 어느 프로토콜에 넘길지 적습니다. 이것이 두 번째 갈림길입니다.
위쪽 프로토콜로 흔히 쓰이는 것은 TCP(Transmission Control Protocol, 전송 제어 프로토콜)와 UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜) 둘입니다. TCP 는 데이터를 빠짐없이 순서대로 전합니다. UDP 는 조각을 하나씩 따로 던집니다. 프로토콜 번호는 TCP 가 6, UDP 가 17 입니다.
TCP 와 UDP 헤더에는 포트 번호가 있습니다. 포트 번호는 한 컴퓨터 안에서 어느 프로그램이 받을지를 가리킵니다. 컴퓨터의 주소가 건물 번지라면 포트 번호는 호수입니다.
포트 번호로 고르는 것이 세 번째 갈림길입니다. 조각은 이 갈림길을 지나 프로그램에 닿습니다.
아래 그림은 웹 요청과 DNS(Domain Name System, 도메인 이름 시스템) 질의가 한 선으로 같이 들어온 경우입니다. DNS 는 도메인 이름을 주소로 바꿔 주는 서비스입니다. 웹 서버는 흔히 443번 포트에서, DNS 서버는 53번 포트에서 받습니다.
flowchart TD
A["도착한 프레임"] -->|"프레임 헤더의 종류 칸"| B["IP"]
B -->|"프로토콜 번호 6"| C["TCP"]
B -->|"프로토콜 번호 17"| D["UDP"]
C -->|"포트 번호 443"| E["웹 서버 프로그램"]
D -->|"포트 번호 53"| F["DNS 서버 프로그램"]
두 조각은 IP 까지 같은 길을 걷습니다. 둘은 프로토콜 번호에서 갈라집니다. 그 뒤 포트 번호로 각자 받을 프로그램을 찾아갑니다. 갈림길이 서 있는 계층은 달라도 묻는 것은 「이 조각을 다음에 누구에게 넘길까」 하나입니다.
조각을 받는 끝, 소켓
포트 번호로 찾아간 끝에는 소켓이 있습니다. 소켓은 프로그램이 네트워크로 데이터를 주고받으려고 운영체제에서 받아 쓰는 창구입니다. 프로그램은 소켓에서 읽고 소켓에 씁니다.
어느 소켓에 넣을지는 운영체제가 정합니다. 프로그램은 소켓을 열고 이 포트 번호로 오는 것을 받겠다고 알립니다. 그러면 운영체제가 그 번호와 소켓을 짝지어 적어 둡니다. 이 일을 바인드라고 부릅니다.
운영체제가 이 짝을 적어 두는 목록을 이 문서에서는 소켓 표라고 부릅니다. 조각이 도착하면 운영체제는 헤더의 번호로 소켓 표를 찾습니다.
UDP 와 TCP 가 소켓을 찾는 열쇠
두 프로토콜은 소켓 표를 찾을 때 쓰는 열쇠가 다릅니다. 이 소절은 같은 포트로 손님 둘이 들어오는 경우를 두고 둘을 견줍니다.
UDP 는 받는 쪽 주소와 받는 쪽 포트 두 값만 봅니다. 손님 둘이 같은 서버의 같은 포트로 보내면 둘 다 같은 소켓에 들어갑니다. 누가 보냈는지는 프로그램이 조각마다 따로 확인합니다.
TCP 는 네 값을 봅니다. 보낸 쪽 주소와 포트, 받는 쪽 주소와 포트입니다. 이 네 값 묶음을 4-튜플이라고 부릅니다. 받는 쪽이 같아도 보낸 쪽이 다르면 다른 소켓에 들어갑니다.
아래 표는 서버의 9000번 포트로 조각 셋이 들어온 경우입니다.
| 조각 | 보낸 쪽 | 받는 쪽 | UDP 라면 | TCP 라면 |
|---|---|---|---|---|
| 가 | 손님 A · 포트 50001 | 서버 · 포트 9000 | 소켓 1 | 소켓 1 |
| 나 | 손님 B · 포트 50001 | 서버 · 포트 9000 | 소켓 1 | 소켓 2 |
| 다 | 손님 A · 포트 50002 | 서버 · 포트 9000 | 소켓 1 | 소켓 3 |
세 조각은 받는 쪽이 전부 같습니다. UDP 는 받는 쪽만 보니 셋을 한 소켓에 넣습니다. TCP 는 보낸 쪽까지 보니 셋을 전부 다른 소켓에 넣습니다. 손님 A 가 보낸 가와 다도 보낸 쪽 포트가 달라서 서로 갈립니다.
TCP 가 네 값을 보는 까닭은 연결 때문입니다. TCP 는 두 끝이 먼저 인사를 나눠 연결을 맺습니다. 데이터는 그 연결 위로 주고받습니다.
연결마다 순서와 빠진 조각을 따로 챙겨야 하니 연결 하나에 소켓 하나를 둡니다. 네 값은 연결 하나를 가리키는 이름표입니다.
연결을 새로 맺자는 요청은 따로 받습니다. 서버 프로그램은 포트 하나에 연결 요청만 받는 리슨 소켓을 열어 둡니다. 요청이 와서 연결이 맺어지면 그 연결 몫의 소켓이 새로 생깁니다. 서버 코드에서 accept 가 돌려주는 것이 이 새 소켓입니다.
넘길 곳이 없을 때
헤더의 번호가 소켓 표에 없으면 넘길 곳이 없습니다. 그 포트를 연 프로그램이 없거나 이미 꺼진 경우입니다. 이때 도착한 조각은 버려집니다. 보낸 쪽에 알림이 돌아가기도 합니다.
TCP 는 연결 요청을 받을 소켓이 없으면 거절하는 조각을 돌려보냅니다. 이 조각에는 RST(reset, 초기화) 표시가 켜져 있습니다.
클라이언트 코드는 이것을 「Connection refused」 오류로 받습니다. 서버를 띄우기 전에 접속하면 보는 그 오류입니다. 운영체제 오류 이름으로는 ECONNREFUSED 입니다.
UDP 는 알림을 ICMP(Internet Control Message Protocol, 인터넷 제어 메시지 프로토콜)에 맡깁니다. ICMP 는 전달이 잘못됐을 때 그 사실을 보낸 쪽에 알리는 프로토콜입니다. 받는 컴퓨터는 ICMP 로 「이 포트에 닿을 수 없다」고 돌려보냅니다. 방화벽이 이 알림을 걸러 내기도 해서 보낸 쪽이 아무 말도 못 듣는 일이 있습니다.
번호 하나에 주인 하나
역다중화가 되려면 번호 하나가 주인 하나를 가리켜야 합니다. 두 프로그램이 같은 포트를 받겠다고 하면 도착한 조각을 누구에게 줄지 정할 수 없습니다. 그래서 운영체제는 이미 쓰이는 포트를 다른 프로그램이 바인드하려 하면 보통 거절합니다. 서버를 두 번 띄울 때 보는 「Address already in use」 오류가 이것입니다.
이 규칙은 포트에만 걸리지 않습니다. 식별자를 읽는 계층이라면 어디서나 같은 번호를 두 곳에 줄 수 없습니다. 프로토콜 번호도 마찬가지입니다. 6 이 TCP 와 다른 프로토콜을 함께 가리키면 IP 는 조각을 어디에 넘길지 못 정합니다.
프로그램 안에서 다시 가르는 일
역다중화는 운영체제가 소켓에 넣는 데서 끝나지 않습니다. 프로그램이 받은 바이트 안에서 한 번 더 갈라내는 경우가 둘 있습니다. HTTP/2 와 리버스 프록시입니다.
HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)는 웹이 요청과 응답을 주고받는 프로토콜입니다. 그 둘째 판인 HTTP/2 는 연결 하나에 요청 여럿을 함께 싣습니다.
HTTP/2 는 요청과 응답 한 쌍을 스트림이라고 부릅니다. 스트림마다 번호가 붙습니다. 받는 쪽은 도착한 조각의 스트림 번호를 보고 어느 요청의 응답인지 가려냅니다. 운영체제가 포트 번호로 한 일을 프로그램이 스트림 번호로 한 번 더 하는 셈입니다.
서버 앞단에서도 비슷한 일이 일어납니다. 리버스 프록시는 서버들 앞에 서서 요청을 대신 받아 뒤의 서버에 넘겨주는 서버입니다. 포트 하나로 들어온 요청을 여러 서비스에 나눠 줍니다.
리버스 프록시는 요청 헤더의 Host 값이나 경로를 보고 어느 서비스로 보낼지 고릅니다. Host 가 blog.example.com 이면 블로그 서버로, 경로가 /images/ 로 시작하면 이미지 서버로 보내는 식입니다. 식별자가 번호가 아니라 이름일 뿐, 도착한 것을 읽고 넘길 곳을 고른다는 점에서 같은 모양의 일입니다.
관련 항목
역다중화와 짝을 이루는 처리 단계
다중화 · 캡슐화 · 역캡슐화 · 프레이밍 · 단편화 · 재조립
역다중화가 읽는 식별자
헤더 · 프로토콜 번호 · 이더타입 · 포트 · 4-튜플 · 5-튜플 · 스트림 · 연결 ID
역다중화가 조각을 넘겨주는 받는 창구
계층마다 역다중화를 하는 프로토콜
이더넷 · IP · TCP · UDP · TLS · HTTP/2 · QUIC
넘길 곳이 없을 때 나는 오류
ECONNREFUSED · EADDRINUSE · RST · ICMP
받은 요청을 다시 나눠 주는 서버
리버스 프록시 · 로드 밸런서 · 가상 호스트 · SNI · NAT
역다중화와 이름이 닮은 개념
입출력 다중화 · 이벤트 루프 · epoll · 리액터 패턴
역다중화가 속하는 상위 분류
다른 이름: demultiplexing · 디멀티플렉싱 · demux