포트
고친 사람 github-actions[bot]
포트는 컴퓨터에 도착한 데이터를 그 안의 어느 프로그램에게 건넬지 가리킵니다. 컴퓨터 한 대에서 프로그램 여럿이 동시에 통신할 수 있는 것이 포트 덕분입니다. 케이블을 꽂는 구멍도 포트라고 부릅니다. 이 글은 앞의 것을 다룹니다.
쉽고 빠른 이해
포트는 컴퓨터에 들어온 데이터를 어느 프로그램에게 줄지 정하는 번호입니다. 웹 서버는 443번, 이름 조회 서버는 53번처럼 프로그램마다 번호를 나눠 맡습니다.
번호가 없으면 받는 쪽은 데이터가 자기 컴퓨터로 온 것까지만 압니다. 그 안의 누구에게 줄지는 모릅니다. 프로그램이 여럿 도는 컴퓨터라면 누구에게 줄지 가릴 방법이 있어야 합니다.
어떻게 도나:
- 받는 프로그램이 번호 하나를 맡겠다고 운영체제에 알립니다
- 보내는 쪽은 컴퓨터 주소와 번호를 함께 적어 보냅니다
- 운영체제는 도착한 데이터의 번호를 보고 그 번호를 맡은 프로그램에게 넘깁니다
대가는 번호를 미리 맞춰 둬야 한다는 것입니다. 한 컴퓨터에서 같은 번호를 두 프로그램이 동시에 맡을 수 없습니다. 중간 장비가 번호를 보고 막으면 연결이 아예 안 됩니다.
상세
아파트 단지로 오는 택배를 떠올리면 됩니다. 동 번호까지만 적힌 상자는 경비실에서 멈춥니다. 호수가 적혀 있어야 집 앞까지 갑니다.
포트는 한 컴퓨터 안에서 통신 상대 프로그램을 가리키는 번호입니다. 프로그램이 뜰 때 맡았다가 내려가면 놓습니다. IP 주소(Internet Protocol address, 인터넷 프로토콜 주소)는 어느 컴퓨터인지까지 알려 줍니다. 포트가 그 안의 어느 프로그램인지를 마저 짚습니다. 주소와 번호를 한 쌍으로 묶은 통신 창구가 소켓입니다.
아래는 주소와 포트가 일을 나눠 맡는 데서 시작해 이름이 겹치는 다른 포트로 끝납니다.
번호는 데이터 앞에 붙는 정보 묶음인 헤더에 실려 갑니다. 헤더에는 이 데이터를 어디서 어디로 보내는지가 적힙니다.
TCP(Transmission Control Protocol, 전송 제어 프로토콜)는 데이터가 빠짐없이 순서대로 닿게 챙기며 실어 나르는 방식입니다. UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)는 그런 보장 없이 보내기만 하는 방식입니다. 둘 다 헤더 앞머리에 출발 포트 칸과 도착 포트 칸을 둡니다.
packet-beta 0-15: "출발 포트" 16-31: "도착 포트"
그림의 두 칸이 각각 16비트입니다. 16비트로 담을 수 있는 수가 육만 개 남짓이라 번호는 0부터 65535까지입니다.
주소와 포트의 분담
IP 주소는 인터넷에서 컴퓨터 한 대를 가리킵니다. 그런데 그 컴퓨터 안에서는 웹 서버와 이름 조회 서버와 원격 접속 서버가 같이 돕니다. 도착한 데이터를 누구에게 줄지는 주소만으로 못 정합니다.
그 갈림을 포트가 맡습니다. 운영체제는 도착한 데이터에서 도착 포트 번호를 읽습니다. 그 번호를 맡은 프로그램에게 데이터를 넘깁니다. 여러 프로그램의 통신을 주소 하나에 겹쳐 싣고 번호로 다시 가르는 이 방식이 다중화입니다.
flowchart TD
P["도착한 데이터 · 도착 포트 443"] --> K
subgraph H["컴퓨터 한 대"]
K["운영체제 · 도착 포트를 읽는다"]
K -.->|"번호 안 맞음 · 안 감"| A["22번 · 원격 접속 서버"]
K -->|"번호 맞음"| B["443번 · 웹 서버"]
K -.->|"번호 안 맞음 · 안 감"| C["53번 · 이름 조회 서버"]
end
그림에서 데이터는 주소를 보고 컴퓨터까지 왔습니다. 그 안에서 갈래는 셋입니다. 데이터가 가는 길은 번호가 맞는 하나뿐입니다.
번호를 나눠 쓰는 관습
번호가 육만 개 넘게 있어도 거는 쪽이 어느 번호로 걸어야 할지 모르면 소용이 없습니다. 그래서 널리 쓰는 서비스는 번호를 미리 정해 두고 씁니다. 웹은 80번과 443번, 이름 조회는 53번, 원격 접속은 22번입니다. 이렇게 미리 약속된 번호가 잘 알려진 포트입니다.
번호 전체는 쓰임에 따라 세 구간으로 나눠 씁니다.
| 구간 | 부르는 이름 | 누가 쓰나 |
|---|---|---|
| 0 ~ 1023 | 잘 알려진 포트 | 널리 쓰는 서비스. 유닉스 계열에서는 관리자 권한이 있어야 맡습니다 |
| 1024 ~ 49151 | 등록 포트 | 제품이나 서비스가 신청해 등록한 번호입니다 |
| 49152 ~ 65535 | 임시 포트 | 연결을 거는 쪽이 그때그때 빌려 쓰는 번호입니다 |
첫 구간에 권한이 필요한 까닭은 신뢰 때문입니다. 아무 프로그램이나 443번을 맡을 수 있으면 그 컴퓨터의 웹 서버 행세를 할 수 있습니다. 이 구간을 따로 특권 포트라고 부릅니다.
구간은 배정 관습이지 강제가 아닙니다. 웹 서버를 8080번으로 띄워도 잘 돕니다. 연결을 거는 쪽이 빌려 쓰는 번호의 범위도 운영체제마다 다릅니다.
그래서 번호를 고르는 기준은 누가 찾아오느냐입니다. 처음 붙는 쪽이 주소만 알고 곧장 찾아와야 하는 서비스는 약속된 번호를 씁니다. 정해진 상대끼리만 붙는 안쪽 서비스는 비어 있는 번호를 아무거나 정해 양쪽 설정에 적어 두면 됩니다.
번호를 맡고 기다리기
프로그램이 어떤 번호로 오는 데이터를 받으려면 그 번호를 맡겠다고 운영체제에 등록해야 합니다. 이 등록을 바인드라고 합니다. 아래는 파이썬에서 8080번을 맡고 접속 하나를 받는 모습입니다.
s = socket.socket()
s.bind(("0.0.0.0", 8080)) # 8080 번 맡기
s.listen()
conn, addr = s.accept()
print(addr) # ('10.0.0.5', 51413)
첫 줄은 앞에서 말한 통신 창구를 하나 만드는 줄입니다. 둘째 줄이 번호를 맡는 줄입니다. 컴퓨터 한 대도 랜과 와이파이처럼 들어오는 길마다 주소를 따로 가집니다. 0.0.0.0 은 그중 어느 주소로 오든 받겠다는 뜻입니다.
listen 은 이제 접속을 받겠다는 선언입니다. accept 는 접속 하나가 붙을 때까지 기다립니다. 마지막 줄이 찍는 두 값은 걸어온 쪽의 주소와 그쪽 포트입니다.
같은 컴퓨터에서 같은 번호를 두 프로그램이 동시에 맡을 수는 없습니다. 이미 맡힌 번호를 또 맡으려 하면 등록이 실패합니다. 서버를 다시 띄울 때 자주 보는 「주소가 이미 사용 중」 오류가 이 실패입니다.
거는 쪽은 번호를 고르지 않아도 됩니다. 운영체제가 남는 번호 하나를 골라 붙여 줍니다. 답장이 돌아올 때 어느 프로그램의 것인지 알아야 하니 거는 쪽에도 번호가 필요합니다. 위 코드가 찍은 51413번이 그렇게 빌려 준 임시 포트입니다.
한 컴퓨터가 밖으로 거는 연결도 무한하지 않습니다. 빌려 줄 49152~65535 구간이 다 차면 새 연결이 막힙니다. 이것이 포트 고갈입니다. 연결을 맺고 곧바로 끊기를 되풀이하는 서비스에서 만납니다.
같은 번호로 여러 연결 받기
웹 서버는 443번 하나로 수천 개의 연결을 동시에 받습니다. 번호는 하나뿐입니다. 그런데도 섞이지 않습니다. 연결 하나를 가리키는 것이 도착 포트 하나가 아니라 네 값이기 때문입니다.
아래 표가 그 네 값입니다. 같은 443번에 붙은 연결 셋이 각각 어떤 값을 갖는지 옆에 함께 적었습니다.
| 값 | 무엇이 들어가나 | 연결 1 | 연결 2 | 연결 3 |
|---|---|---|---|---|
| 출발지 주소 | 걸어온 컴퓨터의 주소 | 10.0.0.5 | 10.0.0.5 | 198.51.100.7 |
| 출발 포트 | 걸어온 쪽이 빌려 쓴 번호 | 51413 | 51414 | 40002 |
| 도착지 주소 | 서버 컴퓨터의 주소 | 203.0.113.9 | 203.0.113.9 | 203.0.113.9 |
| 도착 포트 | 서버가 맡고 있는 번호 | 443 | 443 | 443 |
같은 서버에 붙은 두 클라이언트는 출발지 주소가 다릅니다. 한 클라이언트가 창을 두 개 열어도 출발 포트가 서로 다릅니다. 그래서 네 값을 묶으면 연결마다 값이 달라집니다. 운영체제는 이 묶음을 열쇠 삼아 어느 연결의 데이터인지 가립니다.
포트를 보고 판단하는 중간 장비
이 절은 방화벽과 집 공유기 둘을 봅니다. 둘이 포트 번호를 보고 무엇을 하는지 짚습니다.
방화벽은 지나가는 데이터의 포트 번호를 보고 통과와 차단을 가릅니다. 서버는 멀쩡히 떠 있습니다. 그런데도 밖에서 붙지 못하는 상황이 대개 이것입니다.
반대로 어느 번호가 열려 있는지 바깥에서 하나씩 두드려 보는 일이 포트 스캔입니다.
집이나 사무실의 공유기는 NAT(Network Address Translation, 네트워크 주소 변환)을 합니다. 나가는 데이터의 출발 포트를 자기 번호로 바꿉니다. 답장이 오면 그 번호를 보고 원래 컴퓨터에게 돌려줍니다.
sequenceDiagram
participant 안쪽 as 안쪽 컴퓨터
participant 공유기
participant 바깥 as 바깥 서버
안쪽->>공유기: 출발 포트 51413 으로 보낸다
Note over 공유기: 40001 ↔ 10.0.0.5:51413 을 적어 둔다
공유기->>바깥: 출발 포트를 40001 로 바꿔 보낸다
바깥-->>공유기: 40001 로 답장한다
공유기-->>안쪽: 적어 둔 짝을 보고 51413 으로 돌려준다
Note over 공유기: 적어 둔 짝이 없으면 누구에게 줄지 모른다
밖에서 안쪽 프로그램으로 바로 걸지 못하는 것도 이 때문입니다. 특정 번호로 온 것을 특정 컴퓨터에게 보내라고 미리 적어 두는 설정이 포트 포워딩입니다.
이름이 겹치는 다른 포트
컴퓨터 뒤편의 USB(Universal Serial Bus, 범용 직렬 버스) 구멍이나 랜 케이블 구멍도 포트라고 부릅니다. 이쪽은 번호가 아니라 물리적인 접점입니다. 스위치의 몇 번 포트라고 말할 때도 케이블을 꽂는 구멍을 가리킵니다.
프로그램을 다른 환경으로 옮기는 일도 영어로는 같은 낱말을 씁니다. 한국어로는 포팅이라고 합니다. 이쪽 포트는 통신과 아무 관계가 없습니다.
관련 항목
포트와 함께 목적지를 짚는 이름
IP 주소 · 소켓 · 호스트 · 도메인 · URL · 엔드포인트
포트 번호가 나뉘는 구간
잘 알려진 포트 · 등록 포트 · 임시 포트 · 특권 포트
포트를 헤더에 싣는 프로토콜
TCP · UDP · QUIC · SCTP · 전송 계층 · 헤더
포트를 미리 약속해 둔 서비스
HTTP · HTTPS · DNS · SSH · SMTP · NTP
포트를 맡고 넘기는 프로그래밍 인터페이스
버클리 소켓 · bind · listen · accept · connect · 파일 디스크립터
포트 번호를 바꾸거나 걸러 내는 장비
NAT · 방화벽 · 포트 포워딩 · 프록시 · 로드 밸런서 · 라우터
포트에서 비롯되는 장애와 공격
포트 고갈 · 포트 스캔 · 포트 도달 불가 · 연결 거부 · TIME_WAIT · EADDRINUSE
포트 번호를 배정하고 등록하는 기관과 문서
IANA · RFC 6335 · 서비스 이름
포트라는 이름을 다른 뜻으로 쓰는 분야
하드웨어 포트 · 직렬 포트 · USB · 포팅 · 포트 권한
다른 이름: port · ports · 포트 번호