Nền tảng Mạng máy tính
14/17
Bài 14 / 17~10 phútPort, Socket & DNSMiễn phí… lượt xem

Port & socket — localhost:8080 thực sự là gì

Port định danh ứng dụng, socket là điểm cuối kết nối, 5-tuple định danh một kết nối: vì sao một cổng 443 phục vụ được nghìn client, 0.0.0.0 vs 127.0.0.1, EADDRINUSE.

TL;DR: Tới đúng máy (IP) chưa đủ: một máy chạy nhiều dịch vụ, nên cần port (số 0–65535) để vào đúng ứng dụng. Một socket là điểm cuối giao tiếp gắn với (IP, port). Điều khiến một web server lắng nghe một cổng 443 mà phục vụ được hàng nghìn client cùng lúc là 5-tuple: (protocol, src IP, src port, dst IP, dst port) định danh duy nhất mỗi kết nối, và client khác nhau ở IP/port nguồn nên không lẫn. Bind 127.0.0.1 là chỉ nội bộ, 0.0.0.0 là mọi interface. Hai tiến trình không thể bind cùng IP:port, tiến trình thứ hai nhận EADDRINUSE. Soi bằng ss/lsof.

npm run dev báo "listening on localhost:3000", ssh user@host đi tới cổng 22, EADDRINUSE: address already in use :::8080 — bạn gặp port mỗi ngày. Nhưng localhost:8080 thực sự nghĩa là gì? Và bí ẩn lớn hơn: một web server chỉ lắng nghe cổng 443, làm sao nó phục vụ hàng nghìn người dùng đồng thời mà không lẫn lộn ai với ai?

Bài này giải thích port, socket, và khái niệm 5-tuple — chìa khoá trả lời cả hai câu hỏi.

1. Analogy — Số điện thoại công ty + máy lẻ

Một công ty có một số điện thoại (như IP của máy), nhưng hàng trăm máy lẻ (extension) bên trong. Bạn gọi số công ty rồi bấm "108" để vào đúng phòng. Tổng đài phân biệt hàng trăm cuộc gọi đồng thời nhờ biết ai gọi từ đâu tới máy lẻ nào.

Công tyMạng
Số điện thoại công tyĐịa chỉ IP (đúng máy)
Máy lẻ "108"Port (đúng ứng dụng)
Tổng đài phân biệt từng cuộc5-tuple phân biệt từng kết nối
💡 Cách nhớ

IP đưa bạn tới đúng toà nhà; port đưa tới đúng phòng. localhost:8080 = "máy này, ứng dụng đang nghe ở cổng 8080".

2. Port — số định danh ứng dụng

Port là số nguyên 0–65535 ở tầng Transport, cho biết gói dữ liệu thuộc về ứng dụng nào trên máy. Chia ba nhóm:

NhómDảiVí dụ
Well-known0–102380 (HTTP), 443 (HTTPS), 22 (SSH), 53 (DNS)
Registered1024–491515432 (PostgreSQL), 3306 (MySQL), 6379 (Redis)
Ephemeral (tạm)49152–65535port nguồn OS tự cấp cho client

Khi bạn mở một web, đích là IP:443 (cổng cố định của server), còn nguồn là IP_của_bạn:<port tạm> — OS tự chọn một ephemeral port cho kết nối đó. Port chỉ là con số; thứ OS thực sự cấp cho ứng dụng để dùng con số đó là socket.

3. Socket — điểm cuối kết nối

Socket là giao diện hệ điều hành cấp cho ứng dụng để gửi/nhận qua mạng — gắn với một cặp (IP, port). Có hai vai trò. Listening socket là cái server tạo một lần: gọi bind(IP, port) rồi listen(), nghĩa là "tôi nhận kết nối ở cổng này", ví dụ web server bind 0.0.0.0:443. Connection socket sinh ra cho từng client: mỗi client kết nối tới, accept() ở server (và connect() ở client) tạo một socket riêng cho kết nối đó. Vậy một server có một listening socket nhưng nhiều connection socket, mỗi client một cái. Nhìn thấy cả hai loại trên một web server thật bằng ss:

Bash
ss -tan | head -4
# State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port
# LISTEN  0       511      0.0.0.0:443           0.0.0.0:*
# ESTAB   0       0        10.0.0.5:443          203.0.113.7:51001
# ESTAB   0       0        10.0.0.5:443          198.51.100.9:60200

Dòng LISTEN là listening socket: nó chỉ có địa chỉ cục bộ 0.0.0.0:443, cột Peer để * vì chưa gắn với ai. Hai dòng ESTAB là hai connection socket, mỗi dòng một client đang nối. Nhìn kỹ hai dòng đó: phía server trùng nhau hoàn toàn (10.0.0.5:443), chúng khác nhau chỉ ở IP và port nguồn của client. Bốn giá trị đó cộng với giao thức TCP là bộ định danh mà mục tiếp theo gọi tên.

4. 5-tuple — vì sao một cổng phục vụ nghìn client?

Điểm cốt lõi: 5 thành phần (5-tuple) định danh duy nhất một kết nối TCP:

protocol
TCP
src IP
client
src port
tạm, mỗi kết nối khác
dst IP
server
dst port
443 (cố định)

Hai client cùng kết nối tới server:443. Chúng trùng dst IP và dst port — nhưng khác src IP hoặc src port. Vậy 5-tuple của hai kết nối khác nhau, OS coi chúng là hai kết nối tách biệt, không lẫn. Nhờ thế một cổng 443 phục vụ được vô số client:

Plain Text
TCP  203.0.113.7:51001  ->  10.0.0.5:443    (client A)
TCP  203.0.113.7:51002  ->  10.0.0.5:443    (client A, ket noi 2)
TCP  198.51.100.9:60200 ->  10.0.0.5:443    (client B)

Cả ba cùng 10.0.0.5:443 nhưng khác nguồn, nên là ba kết nối độc lập.

Một listening socket 0.0.0.0:443 ở giữa, ba client đi vào từ trái với nguồn 203.0.113.7:51001, 203.0.113.7:51002 và 198.51.100.9:60200, ba connection socket đi ra bên phải chỉ khác nhau ở IP hoặc port nguồn — 5-tuple khác nhau nên OS coi là ba kết nối độc lập

Thử ngẫmmáy bạn cạn ephemeral port, chỉ còn đúng một cổng nguồn rảnh. Client đó mở được mấy kết nối cùng lúc tới server:443, và 5-tuple giải thích thế nào?

5. localhost, 127.0.0.1 và 0.0.0.0

Khi bind, địa chỉ quyết định ai kết nối được (nối lại bài IP). Bind 127.0.0.1 (localhost) thì chỉ nhận kết nối từ chính máy này, an toàn cho dịch vụ nội bộ như database dev. Bind 0.0.0.0 thì lắng nghe trên mọi interface, kể cả từ máy khác trong LAN hay Internet nếu có đường vào. Bind nhầm 0.0.0.0 cho thứ đáng lẽ chỉ nội bộ là một lỗi bảo mật phổ biến — vô tình mở dịch vụ ra ngoài. Địa chỉ quyết định ai vào được. Còn khi hai tiến trình cùng đòi một địa chỉ, OS không cho cả hai cùng giữ; mục sau là thông báo lỗi của tình huống đó.

6. EADDRINUSE — vì sao "address already in use"

Hai tiến trình không thể cùng bind một cặp IP:port. Chạy hai server trên :8080, cái thứ hai báo EADDRINUSE. Tìm thủ phạm:

Bash
ss -tlnp                 # liet ke socket dang LISTEN + tien trinh
# LISTEN 0 511 0.0.0.0:8080 ... users:(("node",pid=4321,...))
lsof -i :8080            # cach khac, theo cong

Đôi khi cổng vẫn "bận" một lúc sau khi tắt server do trạng thái TIME_WAIT của TCP — chi tiết ở course TCP. Tạm thời: chờ vài giây hoặc dùng option tái sử dụng địa chỉ.

Thử ngẫmbạn restart server ngay sau khi tắt, EADDRINUSE xuất hiện dù ss -tlnp không thấy tiến trình nào khác giữ :8080. Nghi ngờ tiếp theo là gì?

7. Pitfall — hiểu nhầm thường gặp

❌ Nhầm 1: "Một cổng chỉ phục vụ được một client một lúc." ✅ Một listening socket trên cổng 443 đẻ ra nhiều connection socket, mỗi client một cái, phân biệt bằng 5-tuple. Một cổng phục vụ vô số kết nối đồng thời.

❌ Nhầm 2: "localhost:3000 thì ai cũng vào được." ✅ Bind 127.0.0.1:3000 chỉ chấp nhận kết nối từ chính máy đó. Muốn máy khác vào phải bind 0.0.0.0 (và có đường mạng/port forwarding).

❌ Nhầm 3: "EADDRINUSE là bug của code tôi." ✅ Thường là một tiến trình khác (hoặc lần chạy trước chưa thoát hẳn) đang giữ cổng, hoặc TIME_WAIT. Dùng ss -tlnp/lsof -i tìm tiến trình giữ cổng trước khi nghi code.

8. 📚 Deep Dive — tài liệu gốc

📚 Spec & reference chính thức

Đọc khi muốn tới gốc port/socket:

Ghi chú: "Socket" vừa là khái niệm (điểm cuối (IP, port)) vừa là API hệ điều hành. Chi tiết vòng đời socket TCP sẽ học ở course TCP.

9. Tóm tắt

  • Port (0–65535) định danh ứng dụng trên một máy; well-known (0–1023), registered, ephemeral (OS cấp cho client).
  • Socket là điểm cuối (IP, port): một listening socket ở server, nhiều connection socket cho từng client.
  • 5-tuple (protocol, src IP, src port, dst IP, dst port) định danh duy nhất mỗi kết nối — nên một cổng 443 phục vụ được vô số client (khác src).
  • Bind 127.0.0.1 = chỉ nội bộ; 0.0.0.0 = mọi interface (cẩn thận lộ dịch vụ).
  • Hai tiến trình không bind cùng IP:port, cái sau nhận EADDRINUSE; soi bằng ss -tlnp / lsof -i.

10. Tự kiểm tra

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Tới đúng máy bằng IP rồi, vì sao vẫn cần port?
  2. Q2
    Một web server chỉ lắng nghe cổng 443 nhưng phục vụ hàng nghìn client cùng lúc mà không lẫn. Cơ chế nào cho phép?
  3. Q3
    Bind server vào 127.0.0.1 khác 0.0.0.0 thế nào, và vì sao nhầm lẫn có thể thành lỗ hổng?
  4. Q4
    Bạn chạy server thì gặp EADDRINUSE. Nên kiểm tra gì trước khi nghi code?
  5. Q5
    Bạn thấy ss -tan liệt kê một dòng LISTEN và ba dòng ESTAB cùng cổng 443. Dòng nào là listening socket, dòng nào là connection socket, và vì sao chúng cùng tồn tại?
  6. Q6
    Khi bạn mở một kết nối tới server:443, vì sao client không cần tự chọn một cổng cố định, và cổng nguồn đó từ đâu ra?

Bài tiếp theo: DNS — phân giải tên miền hoạt động thế nào

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

DNS — phân giải tên miền hoạt động thế nào