Bài 00 dừng ở câu hỏi này (xem lại): App chạy bình thường khi gọi bằng localhost:8080 trên chính máy mình, nhưng người khác gọi vào thì không được. Chặng mở kết nối hỏng ở đâu?
Lúc đầu mình trả lời câu này hơi mơ màng, đại ý là đổi localhost thành một địa chỉ khác, rồi mở cổng
đó trên máy mình cho đồng nghiệp vào. Câu trả lời đó nghe thì xuôi, vậy nó thiếu ở đâu? Nó thiếu ở hai cụm "địa chỉ
của máy" và "mở cổng", vì mỗi cụm gồm nhiều thứ hơn mình tưởng. Bài này gỡ từng thứ một.
Trên bản đồ Bài 00, bước mở kết nối chỉ là một dòng, ghi rằng máy người dùng mở một kết nối tới server. Dòng đó gồm hai việc. Việc thứ nhất là tìm tới đúng máy, việc thứ hai là tìm tới đúng chương trình trong máy đó. Hình 1 phóng to dòng ấy ra.
Trình duyệt → DNS → [ mở kết nối ] → Mã hoá → Gửi request → …
│
├─ gọi đúng địa chỉ (địa chỉ IP)
├─ firewall cho qua (firewall)
└─ có chương trình listen đúng địa chỉ (port, listen)
Giả sử bạn chạy app Spring Boot trên laptop, mở http://localhost:8080 thấy trang hiện ra,
rồi gửi đúng link đó cho đồng nghiệp ngồi bên cạnh. Họ bấm vào, báo lỗi ngay. Bạn đổi link thành địa chỉ
mạng của laptop, họ thử lại, vẫn không vào được, nhưng lần này trình duyệt quay khá lâu mới báo lỗi. Code
chưa đổi dòng nào, app vẫn chạy bình thường trên máy bạn.
Hai lần đều là "không vào được", vậy sao một lần báo ngay còn một lần chờ lâu? Vì hai triệu chứng khác nhau thường đến từ hai lý do khác nhau. Không biết lý do thì dễ thành Ảnh 1, tức là tắt firewall, đổi port, khởi động lại máy, cái nào ăn thì thôi. Mình cũng từng nói luôn là tắt firewall đi cho nhanh, và tab Firewall cho thấy vì sao làm thế là sai.
Học tới đây mình hỏi lại: bản chất của host là gì nhỉ? Lúc thì thấy 127, lúc thì 192, rồi lại nghe nói card WiFi, mình chả biết đó là một thứ hay nhiều thứ. Từ chỗ rối đó, câu hỏi của bài tách ra thành bốn câu nhỏ:
- Một máy có những địa chỉ IP nào, và vì sao
localhostluôn trỏ về máy của chính người gõ nó? - Trong một máy có hàng chục chương trình, kết nối được giao cho đúng chương trình bằng cách nào?
- Firewall chặn ở đâu, và vì sao phải đủ ba điều kiện thì người khác mới gọi tới được?
- Khi thiếu một điều kiện, triệu chứng trông thế nào, và
refusedkháctimeoutra sao?
Cần đã học trước
Bài này dùng lại mấy thứ của Bài 00, và đi sâu hơn vào bước mở kết nối:
- Bản đồ chín chặng, và chỗ của bước mở kết nối trên đó (Bài 00, tab Bản đồ).
- URL và hai phần host, port: host là tên máy, port là con số đi sau dấu hai chấm (Bài 00, tab URL).
- DNS đổi tên miền thành địa chỉ IP. Mạng chỉ chuyển gói tin tới địa chỉ dạng số, qua các router. TCP mở một đường truyền tin cậy giữa hai máy (Bài 00, tab Trên máy người dùng).
curl, công cụ gửi request từ dòng lệnh, đã dùng ở lab Bài 00.
Lần gửi link đầu hỏng vì nó đưa đồng nghiệp về chính máy của họ, không tới máy bạn. Muốn hiểu chỗ
đó thì phải biết một máy có những địa chỉ IP nào, và vì sao localhost lúc nào cũng quay về máy của
người gõ.
Một máy có nhiều địa chỉ IP
Laptop của bạn có mấy địa chỉ? Lần sửa link đầu tiên thay localhost bằng "địa chỉ của laptop", như
thể laptop chỉ có một địa chỉ. Mình cũng từng gọi 127.0.0.1 là "IPv4 mặc định của máy". Cả hai cách
nghĩ đều sai, và muốn thấy sai ở đâu thì phải có ba định nghĩa cho chuẩn trước đã.
- IP address địa chỉ IP
- Con số định danh một giao diện mạng trên một mạng. Với IPv4, thế hệ địa chỉ phổ biến
nhất, nó dài 32 bit và được viết thành bốn số từ 0 tới 255, như
192.168.10.38. Mạng dùng con số này để biết phải chuyển gói tin tới đâu. - Network interface giao diện mạng
- Điểm mà một máy nối vào một mạng: card WiFi, cổng cắm cáp mạng, hoặc một "card" ảo do phần mềm tạo ra. Mỗi giao diện mang địa chỉ riêng của nó. Một máy có bao nhiêu giao diện thì có bấy nhiêu địa chỉ.
- Loopback giao diện vòng lặp
- Một giao diện ảo không nối với dây hay sóng nào. Gói tin gửi tới nó quay ngược vào chính máy đó và
không bao giờ đi ra ngoài. Địa chỉ của nó là
127.0.0.1.
Ba định nghĩa trên dẫn tới điều gì? Dẫn tới chỗ địa chỉ gắn với từng giao diện mạng chứ không gắn với cả cái máy, như cái card hai cổng ở Ảnh 2. Vì vậy một cái laptop bình thường có ít nhất hai địa chỉ cùng lúc, và hay gặp là ba bốn địa chỉ:
- địa chỉ loopback;
- địa chỉ của card WiFi, do router trong nhà cấp;
- một địa chỉ nữa trên một card ảo, nếu đang bật VPN. VPN là phần mềm nối máy vào một mạng ở xa qua Internet, như thể máy đang ngồi trong mạng đó.
Trên máy mình ngày 22/09, ipconfig in ra hai giao diện là WiFi và một card ảo. Lệnh này không liệt kê
loopback, nên cộng thêm loopback thì máy có ba địa chỉ. Hình 2 vẽ ba địa chỉ đó.
một cái laptop
┌────────────────────────────────────┐
│ lo: giao diện loopback │
│ 127.0.0.1 chỉ trong máy │ ← không dây, không sóng,
│ │ gói tin không ra ngoài
│ wlan0: card WiFi │
│ 192.168.10.38 trong mạng WiFi │ ← router cấp, có thể đổi
│ │
│ card ảo của VPN │
│ 10.8.0.2 trong mạng VPN │
└────────────────────────────────────┘
Ba địa chỉ cùng dẫn vào một máy, vậy dùng cái nào cũng được chứ? Không, vì mỗi địa chỉ chỉ có nghĩa với một nhóm người gọi:
127.0.0.1: chỉ chính máy đó gọi được.192.168.10.38: các máy cùng WiFi gọi được.10.8.0.2: các máy trong mạng VPN gọi được.
Đồng nghiệp ngồi cùng WiFi với bạn, nên với họ chỉ có một địa chỉ dùng được là địa chỉ WiFi của laptop bạn. Hồi học, mình được hỏi máy đang có mấy địa chỉ thì lại đi trả lời chuyện dải 127 có bao nhiêu địa chỉ, tức là sang một câu khác mất rồi. Cái địa chỉ WiFi mà mình bỏ sót lại chính là cái làm đồng nghiệp vào được.
Địa chỉ WiFi còn có một chỗ phải để ý là nó đổi được. Router cho máy thuê địa chỉ có thời hạn, bằng một cơ chế
tên là DHCP, nên khi hết hạn hoặc khi đổi mạng thì máy có thể nhận địa chỉ khác. Mấy ngày học bài này, cùng một
chiếc laptop mà hai lần mình đo ra hai địa chỉ, là 192.168.110.203 và 192.168.10.38. Vì
thế đừng ghi cứng địa chỉ mạng nội bộ vào tài liệu hay cấu hình. Server thật thì cần địa chỉ cố định, còn người
dùng gọi nó bằng tên miền.
Ba dải địa chỉ đặc biệt
Địa chỉ nào cũng đi được ra Internet chứ? Không. Có vài dải địa chỉ được để riêng cho mục đích đặc biệt, và theo quy ước thì router trên Internet không chuyển gói tin mang các địa chỉ đó ra ngoài. Ba dải hay gặp nhất khác nhau ở chỗ chúng có nghĩa trong vùng rộng tới đâu, như Hình 3 xếp ra.
127.0.0.1 → chỉ trong máy này (loopback)
fe80::… → chỉ trong đoạn mạng trực tiếp (link-local, IPv6)
192.168.… → chỉ trong mạng nội bộ (mạng riêng)
10.…
172.16.… tới 172.31.…
Vì sao dòng thứ hai của Hình 3 viết khác hẳn hai dòng kia? Vì nó là địa chỉ IPv6, thế hệ địa chỉ mới, dài 128 bit
và viết bằng các nhóm số thập lục phân ngăn nhau bằng dấu hai chấm. Bài này chỉ cần biết IPv6 cũng có loopback,
viết là ::1, tương đương 127.0.0.1. Còn vì sao có hai thế hệ địa chỉ chạy song song thì
Lịch sử địa chỉ IP kể đầy đủ.
Trong IPv6, địa chỉ bắt đầu bằng fe80 là loại link-local. Loại này chỉ có nghĩa giữa các
thiết bị nối trực tiếp với nhau, kiểu laptop với router WiFi, và nó không đi qua được router đó. Lab Bài 00 đã gặp
một địa chỉ như vậy, khi nslookup in ra địa chỉ của router.
Còn dải 127 thì có bao nhiêu địa chỉ? Mình từng tính là 127⁴, và sai. Trong bốn số của địa chỉ, số đầu bị khóa cứng là 127, còn ba số sau tự do. Mỗi số dài 8 bit, ba số là 24 bit, nên dải này có 2²⁴ địa chỉ, khoảng 16,7 triệu. Ảnh 3 cho thấy con số đó nằm ở đâu trong sổ đăng ký.
/8 chính là "8 bit đầu cố định". Hai dòng khoanh là hai dải vừa gặp: 10.0.0.0/8 dành cho mạng riêng và 127.0.0.0/8 dành cho loopback. Cả khối 127 đều là loopback, dù máy thường chỉ dùng một địa chỉ trong đó là 127.0.0.1.Trong ba dải, dải mạng riêng là dải đụng thẳng vào tình huống của bài. Vì nó không ra Internet nên đồng nghiệp
cùng WiFi gọi được 192.168.10.38, còn một người đang ở nhà họ thì không bao giờ gọi được. Lý do là
không router nào trên đường đi chịu chuyển gói tin tới một địa chỉ mạng riêng.
Cũng vì thế mà server thật phải nằm ở chỗ có địa chỉ IP công khai, kiểu server thuê trong trung tâm dữ liệu, chứ không chạy trên laptop ở nhà. Lịch sử dẫn tới chuyện này, gồm việc địa chỉ IPv4 hết dần và router gia đình cho cả nhà dùng chung một địa chỉ, nằm ở Lịch sử địa chỉ IP. Còn "cùng mạng" nghĩa là gì, và cái router trong nhà gồm những thiết bị nào, thì Mạng và thiết bị mạng nói rõ.
Mọi điều ở trên đều có văn bản chuẩn đứng sau. Các chuẩn của Internet được công bố thành những văn bản đánh số, gọi là RFC.
- RFC 1122 quy định dải loopback
127.0.0.0/8, kèm yêu cầu địa chỉ dạng này không bao giờ xuất hiện bên ngoài một máy. - RFC 1918 dành ra ba dải mạng riêng:
10.0.0.0/8,172.16.0.0/12và192.168.0.0/16. - RFC 4291 định nghĩa loopback
::1và dải link-localfe80::/10của IPv6. - RFC 6761 xếp cái tên
localhostvào nhóm tên đặc biệt, luôn chỉ về loopback.
Vì sao localhost là một từ tương đối
Có đủ các địa chỉ rồi thì giải thích được lần gửi link đầu. localhost là một cái tên, và máy nào
cũng dịch nó về loopback của chính nó, tức 127.0.0.1, hoặc ::1 trên nhiều hệ
thống mới. Vậy khi đồng nghiệp gõ http://localhost:8080 thì chuyện gì xảy ra? Máy họ dịch tên đó ra
loopback của máy họ, nên gói tin đi một vòng qua giao diện loopback rồi quay lại chính máy họ, chưa hề ra khỏi bàn
của họ. Máy họ không có app nào ở port 8080, nên họ bị từ chối ngay tại đó.
máy bạn máy đồng nghiệp
┌──────────────────────┐ ┌──────────────────────┐
│ app ở port 8080 │ │ không có app nào │
│ │ │ │
│ localhost:8080 │ │ localhost:8080 │
│ └→ 127.0.0.1 ──┐ │ │ └→ 127.0.0.1 ──┐ │
│ tới app ←───┘ │ │ bị từ chối ←┘ │
└──────────────────────┘ └──────────────────────┘
gói tin của đồng nghiệp không bao giờ đi qua đây
localhost quay về chính máy đó.Như vậy localhost không chỉ một máy cố định nào. Nó giống chữ "ở đây", ai nói thì "ở đây" là chỗ
người đó đang đứng, nên gửi cho đồng nghiệp chữ "ở đây" thì họ hiểu là chỗ họ ngồi. Muốn họ gọi tới máy mình thì
phải đưa một địa chỉ của máy mình mà họ với tới được, tức địa chỉ trong mạng chung của hai người.
Port: con số chọn chương trình
Gói tin đã tới đúng máy rồi thì chương trình nào trong máy nhận nó? Một server bình thường chạy cùng lúc nhiều chương trình nhận kết nối từ mạng, ví dụ một web server, một app Spring Boot và một database PostgreSQL. Địa chỉ IP chỉ đưa gói tin tới máy, chứ không nói chương trình nào trong máy sẽ nhận. Vì vậy mỗi gói tin phải mang thêm một con số thứ hai, để hệ điều hành biết giao nó cho ai.
- Port cổng
- Con số 16 bit, từ 0 tới 65535, nằm trong phần đầu của mỗi gói tin TCP. Hệ điều hành của máy nhận
dùng con số này để chọn chương trình sẽ nhận gói tin. Một kết nối luôn nhắm tới một cặp địa chỉ IP
và port, viết là
192.168.10.38:8080.
Vì port chỉ là một trường số 16 bit nên nó không bao giờ lớn hơn 65535. Trong khoảng đó, vài con số được quy ước cho những dịch vụ quen, như 80 cho HTTP, 443 cho HTTPS, 5432 cho PostgreSQL. Còn 8080 thì chỉ là thói quen của dân web khi chạy app trên máy mình, vì nó dễ nhớ và không đụng mấy con số quy ước kia.
Listen: chương trình xin nhận kết nối
Gói tin mang số 8080 tới máy thì có chắc là có người nhận không? Không chắc. Nó chỉ có chỗ đi khi trước đó đã có một chương trình nói với hệ điều hành rằng nó muốn nhận kết nối tới port 8080. Việc đăng ký nhận đó có tên riêng là listen, và điều kiện có chương trình listen đúng địa chỉ trong Hình 1 chính là nói về nó.
- Listen lắng nghe
- Việc một chương trình đang chạy xin hệ điều hành: "mọi kết nối tới port này hãy giao cho tôi". Hệ điều hành ghi lời xin đó vào một bảng các port đang được listen. Không chương trình nào listen một port thì port đó không có trong bảng.
Ai giữ cái bảng đó? Phần lõi của hệ điều hành, có tên là kernel. Ở bài này chỉ cần biết kernel dùng bảng ra sao. Khi có kết nối tới, nó tra bảng xem port đích có trong đó không. Nếu có thì nó giao kết nối cho chương trình tương ứng, còn nếu không có thì nó từ chối.
Bảng đó xem được. Trên Windows, lệnh netstat -ano in nó ra, mỗi dòng LISTENING là một
port đang có chương trình nhận. Cột cuối của mỗi dòng là PID, con số mà hệ điều hành gán cho mỗi chương trình đang
chạy.
$ netstat -ano | grep ":8080 " | grep LISTEN
TCP 127.0.0.1:8080 0.0.0.0:0 LISTENING 25800
127.0.0.1. Chạy trên máy mình, Git Bash, 03/10/2026.Hai chương trình có cùng listen một port được không? Không, nếu là trên cùng một địa chỉ, vì khi đó hệ điều hành
không biết giao kết nối cho ai. Chương trình đến sau bị từ chối ngay lúc xin, kèm lỗi
address already in use.
Listen address: nhận kết nối gọi tới địa chỉ nào
Vì sao dòng LISTENING ở Hình 5 ghi cả địa chỉ lẫn port, là 127.0.0.1:8080? Vì lúc xin
listen, chương trình còn chọn luôn nó nhận kết nối gọi tới địa chỉ nào của máy. Tab trước đã cho thấy một máy có
nhiều địa chỉ, và chương trình được quyền chỉ nhận ở vài địa chỉ trong số đó.
- Listen address địa chỉ listen
- Địa chỉ IP mà chương trình gắn port của nó vào khi listen. Listen
127.0.0.1nghĩa là chỉ nhận kết nối gọi tới loopback, tức chỉ từ trong máy. Listen một địa chỉ cụ thể như192.168.10.38nghĩa là chỉ nhận kết nối gọi tới địa chỉ đó. Listen địa chỉ đặc biệt0.0.0.0nghĩa là nhận kết nối gọi tới mọi địa chỉ của máy.
Có định nghĩa này rồi thì phát biểu được điều kiện listen cho chính xác. Lấy một kết nối gọi tới
192.168.10.38:8080 làm ví dụ. Kết nối đó chỉ được giao cho một chương trình khi chương trình ấy đang
listen port 8080 trên chính địa chỉ 192.168.10.38, hoặc trên 0.0.0.0. Một chương trình
listen 127.0.0.1:8080 thì không nhận kết nối này, dù nó vẫn chạy bình thường và bạn gọi
localhost:8080 vẫn thấy trang.
Ví dụ thật ở Hình 6 chạy cùng một server nhỏ hai lần, chỉ đổi địa chỉ listen.
listen 127.0.0.1 listen 0.0.0.0
$ curl http://127.0.0.1:8080 $ curl http://127.0.0.1:8080
xin chao tu 127.0.0.1 xin chao tu 0.0.0.0
$ curl http://192.168.10.38:8080 $ curl http://192.168.10.38:8080
curl: (7) Failed to connect … xin chao tu 0.0.0.0
after 2039 ms
127.0.0.1 thì gọi
bằng địa chỉ mạng của chính máy cũng bị từ chối. Chạy trên máy mình ngày 03/10/2026, output rút
gọn; địa chỉ mạng nội bộ thật được thay bằng 192.168.10.38 cho khớp các hình trước. Lab 3
làm lại thí nghiệm này.Vậy app của bạn đang listen ở đâu? Spring Boot mặc định listen trên mọi địa chỉ của máy, và muốn đổi thì thêm một
dòng cấu hình là thuộc tính server.address. Nhiều database và công cụ dev thì làm ngược lại, mặc định
chỉ listen 127.0.0.1 cho an toàn.
Hai điều kiện đã có là gọi đúng địa chỉ và có chương trình listen đúng địa chỉ. Điều kiện còn lại nằm giữa hai cái đó trên đường đi của gói tin. Ghép đủ ba thì trả lời được firewall chặn ở đâu, và vì sao phải đủ cả ba thì người khác mới gọi tới được.
Firewall: lớp chặn đứng trước chương trình
Vì sao cần thêm một lớp chặn nữa? Vì một máy nối mạng nhận gói tin từ mọi phía, kể cả từ những máy nó không quen. Nếu không có gì chắn thì ai gửi tới cũng chạm được vào mọi chương trình đang listen. Vì vậy hệ điều hành và thiết bị mạng có thêm một lớp lọc, đứng trước bảng listen. Windows bật sẵn lớp này từ lúc cài máy, và ở tình huống mở bài thì nó là lý do hay gặp nhất làm lần thử thứ hai phải chờ lâu.
- Firewall tường lửa
- Bộ lọc gói tin theo luật, mỗi luật xét nguồn, đích, port và giao thức của gói tin để quyết định cho qua hay chặn. Nó làm việc trước khi gói tin tới được chương trình đang listen. Firewall có thể là một phần mềm trong hệ điều hành, như Windows Defender Firewall, hoặc một thiết bị riêng đứng ở ranh giới giữa hai mạng.
Firewall chặn một gói tin bằng cách nào? Có hai cách, và mỗi cách để lại một triệu chứng khác ở phía người gọi.
- Vứt bỏ lặng lẽ (drop): gói tin biến mất, không có gì gửi lại.
- Từ chối (reject): firewall gửi lại một lời báo rằng kết nối không được phép.
Windows chọn cách nào? Với kết nối đi vào mà không có luật cho phép, firewall của Windows mặc định vứt lặng lẽ. Nhiều firewall khác cũng làm vậy, vì vứt lặng lẽ thì người lạ không biết ở đó có máy nào. Còn vì sao "vứt lặng lẽ" lại thành một lần chờ lâu thì tab sau giải thích.
Ba điều kiện để người khác gọi tới được
Tới đây đã đủ ba điều kiện của bước mở kết nối. Gói tin từ máy đồng nghiệp tới app của bạn gặp chúng theo đúng thứ tự của Hình 7, là gọi đúng địa chỉ, rồi firewall cho qua, rồi có chương trình listen đúng địa chỉ. Thiếu điều kiện nào thì kết nối hỏng ở đó, và mỗi điều kiện hỏng theo một kiểu riêng.
máy đồng nghiệp
│ gói tin tới 192.168.10.38:8080
▼
gọi đúng địa chỉ? không → tới sai máy, bị máy đó từ chối
▼
firewall cho qua? không → bị vứt lặng lẽ, người gọi chờ tới hết giờ
▼
có chương trình listen không → hệ điều hành từ chối ngay
port 8080 trên đúng
địa chỉ này?
▼
app Spring Boot nhận kết nối
| Điều kiện | Cụ thể là | Thiếu thì thấy gì |
|---|---|---|
| Gọi đúng địa chỉ | Gọi địa chỉ của máy bạn trong mạng chung, như 192.168.10.38:8080, không phải localhost | refused, ở chính máy người gọi |
| Firewall cho qua | Firewall trên máy bạn cho gói tin từ ngoài vào port 8080 | timeout |
| Có chương trình listen đúng địa chỉ | App listen trên địa chỉ được gọi tới hoặc trên 0.0.0.0, không chỉ 127.0.0.1 | refused |
Bảng 1 giải thích tình huống mở bài thế nào? Link localhost thiếu điều kiện gọi đúng địa chỉ, nên
đồng nghiệp gọi vào chính máy họ và bị từ chối ngay. Link có địa chỉ WiFi thì gọi đúng địa chỉ rồi, nhưng lại thiếu
điều kiện firewall cho qua. Firewall trên laptop bạn vứt gói tin lặng lẽ, nên trình duyệt của họ phải chờ.
Bảng còn một cái bẫy. Gọi sai địa chỉ và không có chương trình listen cho ra cùng một triệu chứng là
refused, nên gặp refused thì phải kiểm cả hai.
Bảng 1 cũng dùng để kiểm một lần sửa. Mình từng nghĩ mở firewall xong là đồng nghiệp vào được, mà không chắc đâu.
Giả sử bạn mở firewall cho port 8080 rồi mà họ vẫn không vào. Nếu triệu chứng đổi từ timeout sang
refused thì phần firewall đã sửa đúng, vì gói tin giờ qua được firewall và dừng ở điều kiện kế tiếp,
là app đang chỉ listen 127.0.0.1.
Vậy mỗi lần sửa đúng một điều kiện thì triệu chứng đổi sang triệu chứng của điều kiện kế tiếp. Triệu chứng đổi chính là bằng chứng rằng điều kiện vừa sửa đã đạt.
Gặp lỗi mạng thì đừng tắt firewall cho nhanh (mình cũng từng nói vậy). Cứ mở đúng một port, cho
đúng nguồn cần thiết. Trên Windows, lệnh dưới chạy trong PowerShell (cửa sổ dòng lệnh của Windows, mở
bằng quyền quản trị). Nó chỉ cho các máy cùng mạng với máy bạn gọi vào port 8080 (nghĩa của
LocalSubnet), mọi thứ khác vẫn bị chặn như cũ:
New-NetFirewallRule -DisplayName "Spring Boot dev 8080" `
-Direction Inbound -LocalPort 8080 -Protocol TCP `
-RemoteAddress LocalSubnet -Action Allow
Nguyên tắc đứng sau là least privilege, đặc quyền tối thiểu: mỗi thành phần chỉ được cấp đúng phần quyền nó cần.
Ẩn dụ hỗ trợ ghi nhớ: một tòa nhà
Cả bốn khái niệm đã có định nghĩa và ví dụ thật, vậy cần thêm phép so sánh để làm gì? Để giữ bốn vai tách nhau trong đầu, sao cho lúc gặp lỗi thì hỏi đúng bốn câu theo đúng thứ tự. Phép so sánh dưới đây chỉ làm việc đó, nó không thay cho định nghĩa.
địa chỉ IP = một lối vào của tòa nhà (tòa nhà có nhiều lối vào)
port = số phòng bên trong tòa nhà
chương trình listen = người ngồi trong phòng, tự chọn tiếp khách
đến từ lối vào nào (listen address)
firewall = bảo vệ ở lối vào, lọc ai được đi vào
Chữ "phòng" gợi ý port là một không gian có sẵn trong máy. Thực tế không có căn phòng nào: port chỉ là con số 16 bit trong phần đầu gói tin, như Ảnh 4 cho thấy, và hệ điều hành dùng con số đó để tra bảng listen. "Lối vào" cũng gợi ý một thứ vật lý, trong khi một máy có thể tạo ra hàng chục giao diện mạng ảo bằng phần mềm. Khi ẩn dụ và định nghĩa mâu thuẫn, định nghĩa thắng.
Người khác gọi tới được app của bạn khi và chỉ khi đủ ba điều kiện: gọi đúng địa
chỉ, firewall cho qua, có chương trình listen đúng địa chỉ. Gọi sai địa chỉ hoặc không có chương trình
listen cho ra refused; firewall chặn thường cho ra timeout. Sửa đúng một điều
kiện thì triệu chứng đổi.
Một kết nối TCP được mở bằng ba gói tin
Ba điều kiện đủ hay thiếu thì người gọi nhìn thấy gì? Muốn trả lời thì phải xem một kết nối được mở ra sao. TCP,
giao thức mở một đường truyền tin cậy giữa hai máy đã gặp ở Bài 00, không gửi dữ liệu ngay. Trước khi gửi byte nào,
hai máy phải thoả thuận rằng cả hai đã sẵn sàng, và thoả thuận đó gồm ba gói tin trao đổi qua lại. Thoả thuận này
hỏng được theo hai kiểu: một kiểu báo lỗi ngay, tên là refused, và một kiểu bắt người gọi chờ, tên là
timeout.
- Three-way handshake bắt tay ba bước
- Cách TCP mở một kết nối. Bên gọi gửi một gói
SYN("tôi muốn mở kết nối tới port này"). Nếu có chương trình đang listen port đó, máy bên kia trả lờiSYN-ACK("đồng ý"). Bên gọi đápACK("đã nhận"), và kết nối được thành lập.
SYN, B trả SYN,ACK, A đáp ACK, và cả hai sang trạng thái ESTABLISHED, tức kết nối đã mở. Chữ LISTEN ở đầu cột B chính là trạng thái listen của tab Port & listen.Ảnh 7 vẽ lại đúng ba gói tin đó bằng hai cái lon nối dây.
Kết nối mở xong rồi thì hệ điều hành nhận ra nó bằng gì? Bằng bốn con số, gồm địa chỉ và port của bên gọi, địa chỉ và port của bên nhận, gọi chung là bộ bốn. Port của bên nhận phải cố định và biết trước, như 443 hay 8080, vì nếu không thì bên gọi không biết gọi vào đâu.
Còn port của bên gọi thì ai chọn? Chương trình không tự chọn, mà hệ điều hành lấy một số còn trống gắn vào kết
nối, tới khi đóng kết nối thì thu hồi. Vì chỉ dùng tạm như vậy nên nó có tên là ephemeral port, port tạm.
Dòng dưới đây lấy từ output curl -v ở lab Bài 00, và nó in đủ bộ bốn của một kết nối thật.
* Established connection to example.com (104.20.23.154 port 443)
└─────┬─────┘ └┬┘
địa chỉ bên nhận port bên nhận
from 192.168.10.38 port 55289
└─────┬─────┘ └─┬─┘
địa chỉ bên gọi port tạm: hệ điều hành tự cấp
curl -v ở lab Bài 00,
chạy trên máy mình ngày 24/09/2026.Nhờ port tạm mà mười tab trình duyệt cùng mở example.com vẫn là mười kết nối riêng, vì mỗi tab có một
port tạm khác.
Refused: có người trả lời "không"
Kiểu hỏng thứ nhất xảy ra khi nào? Khi gói SYN tới đúng một máy đang chạy, nhưng trên máy đó không
chương trình nào listen port đích. Máy đó không im lặng. Kernel của nó, tức phần lõi hệ điều hành, chủ động gửi lại
một gói đặc biệt để bên gọi biết kết nối không mở được.
- RST gói reset
- Gói TCP mang cờ
RST, nghĩa là "kết nối này không tồn tại". Hệ điều hành gửi nó khi một góiSYNtới một port không có trong bảng listen. - Refused bị từ chối
- Triệu chứng mà bên gọi thấy khi nhận được gói
RST: thư viện mạng dịch nó thành lỗi Connection refused, vàcurlin ra mã lỗi(7).
Chỗ cần nhớ nhất là refused chỉ xảy ra khi máy đích còn sống, vì phải có hệ điều
hành đang chạy thì mới gửi được gói RST. Mình từng nghĩ ngược lại, rằng máy còn sống thì không bao giờ
bị refused. Mình cũng từng nói không ai listen thì người gọi thấy timeout, và cũng sai, vì không ai listen mà gói
tin tới được máy thì chính là trường hợp này.
Vậy nhận được refused thì biết thêm điều gì? Biết chắc hai điều. Một là gói tin đã tới một máy đang
chạy. Hai là trên máy đó không có chương trình nào listen port ấy trên địa chỉ ấy. Trong Bảng 1, đó là hai trường
hợp gọi sai địa chỉ, khi gói tin tới một máy khác như chính máy người gọi, và không có chương trình listen đúng địa
chỉ.
Timeout: không ai trả lời cả
Kiểu hỏng thứ hai là khi gói SYN đi rồi mà không có gì quay về. Hoặc firewall đã vứt nó lặng lẽ,
hoặc không có máy nào mang địa chỉ đó. Lúc ấy bên gọi làm gì? Nó không biết chuyện gì đã xảy ra, nên chỉ làm được
một việc là gửi lại SYN, mỗi lần chờ lâu hơn lần trước, cho tới khi hết thời gian chờ do chính nó
đặt.
- Timeout hết giờ chờ
- Triệu chứng mà bên gọi thấy khi chờ quá thời hạn mà không nhận được câu trả lời nào.
curlin ra mã lỗi(28); thời hạn chờ của nó đặt bằng tham số--max-time.
refused: máy sống, không ai listen
curl ── SYN ──────────────────→ hệ điều hành của máy đích
curl ←────────────────── RST ── "port này không có ai"
→ có câu trả lời → curl tự kết luận → lỗi (7)
timeout: không ai trả lời
curl ── SYN ──────────────────→ 10.255.255.1 (không có máy nào)
curl ── SYN (gửi lại) ────────→ …
curl ── SYN (gửi lại) ────────→ …
… im lặng … hết --max-time → curl bỏ cuộc → lỗi (28)
Phân biệt bằng người quyết định dừng, không bằng tốc độ
Phân biệt hai triệu chứng bằng gì? Cách dễ nghĩ tới nhất là theo tốc độ, refused thì nhanh còn
timeout thì chậm. Cách đó nghe hợp lý nhưng không đáng tin, và lab cho thấy lý do: trên Windows, một
lần refused tới chính máy mình mất hơn hai giây. Tiêu chí vững hơn là hỏi ai quyết định lúc
dừng.
refuseddừng vì bên gọi nhận được câu trả lời. Nó có điểm kết thúc riêng, đặt--max-timebao nhiêu cũng vậy.timeoutdừng vì bên gọi tự bảo mình thôi chờ. Thời gian của nó đúng bằng con số bạn đặt, khớp từng mili-giây.
refused → curl dừng vì nhận được câu trả lời
có điểm kết thúc của riêng nó, không phụ thuộc vào ta
timeout → curl dừng vì ta bảo nó dừng
kéo dài đúng bằng --max-time: đặt 5 thì 5 giây, đặt 15 thì 15
--max-time rồi xem
thời gian của lệnh nào thay đổi theo.Quy tắc này có ngoại lệ nào không? Có một. Vài firewall được cấu hình để từ chối thay vì vứt, và chúng tự gửi lời
từ chối thay cho máy đích, nên bên gọi cũng thấy refused. Vì vậy nói cho chính xác thì phải thêm chữ
"thường". timeout thường chỉ về firewall vứt lặng lẽ hoặc một địa chỉ không có máy nào, còn
refused thường chỉ về một máy còn sống mà không ai listen port đó.
Hành vi "gửi RST khi không có ai listen" được quy định trong đặc tả TCP hiện hành,
RFC 9293: khi một gói TCP tới một kết nối không tồn tại, bên nhận trả lời bằng một
gói reset. Trong Java, gói reset ấy hiện ra thành
java.net.ConnectException: Connection refused, lỗi quen thuộc khi app không kết nối được
database.
Khác mạng thì sao: VPN
Mọi thứ từ đầu bài tới giờ đều giả định người gọi ngồi cùng mạng với bạn. Có lần mình chỉ cho một người bạn cách để hai máy cùng mạng gọi được port 8080 của nhau, và bạn ấy hỏi tiếp: khác mạng thì sao? Mình không biết, tra trên mạng thì thấy người ta bảo dùng VPN server. Mục này trả lời câu hỏi đó bằng đúng những thứ bài đã có.
Trước hết, vì sao khác mạng thì không gọi được? Giả sử người bạn đó ngồi ở nhà của họ và gọi
192.168.10.38:8080. Địa chỉ đó thuộc dải mạng riêng, nên gói tin đi ra router nhà họ rồi không
router nào trên Internet chịu chuyển tiếp. Không ai trả lời, nên họ thấy timeout. Còn địa chỉ công
khai của nhà bạn thì thuộc về router trong nhà bạn, mà router ấy bỏ mọi kết nối do người ngoài tự mở vào. Chuyện
router này được kể kỹ ở Mạng và thiết bị mạng.
Có ba lối để người ở mạng khác vào được. Lối thứ nhất là dặn router chuyển một port từ ngoài vào thẳng laptop, gọi là port forwarding, và làm vậy thì app lộ ra cho cả Internet. Lối thứ hai là đưa app lên một server có địa chỉ công khai. Lối thứ ba là VPN, hợp nhất khi chỉ muốn vài người quen vào được máy mình.
VPN mở đường bằng cách nào? Nó thêm cho mỗi máy một card mạng ảo, chính là cái card thứ ba trong
Hình 2. Sau khi hai người cài cùng một phần mềm VPN và đăng nhập vào cùng một mạng
VPN, laptop bạn có thêm địa chỉ 10.8.0.2, còn máy bạn ấy có thêm 10.8.0.3. Card thật thì
đẩy gói tin ra dây hoặc ra sóng. Card ảo không có dây hay sóng, nên gói tin giao cho nó sẽ đi vào phần mềm VPN, vì
card ảo chính là phần mềm đó.
Phần mềm VPN làm gì với gói tin ấy thì theo một lần bạn ấy mở http://10.8.0.2:8080 là thấy.
- Máy bạn ấy thấy đích
10.8.0.2cùng mạng với card ảo của nó, nên giao gói tin cho card ảo. - Phần mềm VPN mã hoá gói tin rồi bỏ cả gói vào trong một gói mới. Gói mới ghi nơi nhận là địa chỉ công khai của VPN server, và đi ra bằng card WiFi thật như mọi gói tin lên Internet.
- VPN server biết
10.8.0.2là laptop bạn, vì laptop bạn cũng đang giữ một kết nối tới nó. Server đẩy gói tin vào kết nối đó. - Phần mềm VPN trên laptop bạn bóc gói ngoài, giải mã, rồi đưa gói gốc cho hệ điều hành, y như gói vừa đi vào từ card ảo.
- Hết việc của VPN. Hệ điều hành xét ba điều kiện như mọi kết nối khác: firewall có
cho qua không, có chương trình nào listen ở
10.8.0.2:8080không.
máy bạn ấy laptop bạn
gọi 10.8.0.2:8080 ai listen 10.8.0.2:8080?
│ 1 ▲ 5
card ảo 10.8.0.3 card ảo 10.8.0.2
│ 2 mã hoá, bọc vào gói mới ▲ 4 bóc gói ngoài, giải mã
card WiFi ──► router ──► VPN server ──► router ──► card WiFi
3
10.8.0.2 nằm bên trong nó.Trong năm bước có ba thứ mang chữ VPN, và rất dễ gọi lẫn. Hai thứ đầu là phần mềm VPN chạy trên hai máy. Thứ ba là VPN server, một máy ở ngoài Internet có địa chỉ công khai mà hai phần mềm kia cùng gọi tới. Vì cả hai máy đều là bên gọi ra nên router của cả hai nhà đều cho qua, và nhờ vậy VPN đi được qua chỗ mà một kết nối gọi thẳng bị chặn. Vài dịch vụ VPN còn cho hai máy nối thẳng với nhau sau khi server giới thiệu, nhưng ý chính không đổi.
VPN là viết tắt của Virtual Private Network, mạng riêng ảo. RFC 4949 định nghĩa nó là "a restricted-use, logical … computer network that is constructed from the system resources of a relatively public, physical … network (e.g., the Internet)". Tạm dịch là một mạng chỉ dành cho một nhóm, không có thật về phần cứng, dựng trên tài nguyên của một mạng thật và công cộng như Internet.
VPN chỉ lo mang gói tin tới được laptop bạn. Có ai nhận gói tin đó không thì vẫn do listen address quyết định. Mình từng nghĩ khác, rằng bật VPN lên thì app sẽ tự listen ở địa chỉ của VPN. Sai, vì app chọn địa chỉ listen một lần lúc khởi động, còn VPN chỉ thêm cho máy một địa chỉ chứ không đụng gì tới app. Bảng 2 đặt bốn cách listen cạnh ba người gọi.
| App listen ở | Chính laptop gọi 127.0.0.1 | Máy cùng WiFi gọi 192.168.10.38 | Máy trong mạng VPN gọi 10.8.0.2 |
|---|---|---|---|
127.0.0.1 | được | refused | refused |
192.168.10.38 | refused | được | refused |
10.8.0.2 | refused | refused | được |
0.0.0.0 | được | được | được |
Dòng cuối là lý do app Spring Boot để mặc định thì vào được từ cả ba phía. Dòng thứ ba là cách cho riêng người
trong mạng VPN vào, bằng cách đặt server.address thành địa chỉ VPN. Mọi ô còn lại đều là
refused chứ không phải timeout, vì gói tin tới được máy, máy còn sống, và không ai listen
ở địa chỉ đó nên hệ điều hành từ chối ngay. timeout chỉ xuất hiện khi gói tin không tới nơi, ví dụ khi
bạn ấy thoát phần mềm VPN rồi vẫn gọi 10.8.0.2.
Các lab dưới đây đổi bốn câu hỏi của bài thành số đo. Lab 1 là output của buổi học ngày 24/09/2026, còn Lab 2 và Lab 3 mình chạy lại trên cùng máy ngày 03/10/2026 khi tách bài. Tất cả chạy trong Git Bash trên Windows, nên con số trên máy bạn sẽ hơi khác.
Lab 1: tự gây refused, rồi tự gây timeout
Làm sao tự tạo ra một lần refused? Gọi port 9999 của chính máy mình. Máy chắc chắn đang sống, nhưng
không chương trình nào listen port đó, nên đủ đúng hai điều kiện của refused.
Còn timeout thì tạo bằng cách gọi 10.255.255.1. Địa chỉ này thuộc dải mạng riêng
10.x, và trong mạng gia đình bình thường không có máy nào mang nó. Vì gói SYN không tới
máy nào nên không có gì quay về. Đây là cách an toàn để tự tạo một timeout mà không làm phiền máy của
ai.
Trong hai lệnh còn ba thứ chưa gặp:
-4buộccurlchỉ dùng IPv4.-o /dev/nullbỏ phần nội dung nhận được, vì phép đo này chỉ cần biết lỗi và thời gian.timeđo tổng thời gian chạy.
time curl -4 -o /dev/null http://127.0.0.1:9999
time curl -4 -o /dev/null --max-time 5 http://10.255.255.1:9999
127.0.0.1:9999 → curl: (7) Failed to connect … after 2076 ms real 2.155s
10.255.255.1:9999 → curl: (28) Connection timed out after 5007 ms real 5.066s
Hai con số trong ngoặc nghĩa là gì? Mã (7) là "không kết nối được", còn mã (28) là "hết
giờ chờ". Tên chính thức của hai mã này trong thư viện của curl là CURLE_COULDNT_CONNECT
và CURLE_OPERATION_TIMEDOUT.
Con số đáng nhìn nhất là 2076 ms của lệnh refused, vì nó chậm hơn nhiều so với lý thuyết
"có người trả lời ngay". Mình ghi lại đúng chỗ này: refused tới chính máy mình mà mất hơn 2 giây.
Vì sao Windows mất hai giây cho một lần bị từ chối
Hai giây đó từ đâu ra? Trên Linux, khi nhận gói RST từ chính máy mình, hệ điều hành báo lỗi cho
chương trình gần như ngay, thường sau vài mili-giây. Windows làm khác, và nhiều người đã quan sát thấy nó
thử lại vài lần rồi mới kết luận kết nối bị từ chối, kể cả khi đích là chính máy mình. Trang này
không dẫn được tài liệu chính thức của Microsoft cho con số hai giây, nên hãy coi đó là một quan sát đo được, chưa
phải quy định.
Có phải do localhost thử ::1 trước không? Không, vì lệnh trên đã dùng -4
và gõ thẳng 127.0.0.1. Con số 2,155 giây là của Windows.
Lab 2: chứng minh timeout do bạn quyết định
Hình 11 nói timeout dài đúng bằng con số bạn đặt, vậy thử đổi con số đó xem. Chỉ đổi 5
thành 15 rồi chạy lại cả hai lệnh với cùng thời hạn chờ. Nếu Hình 11 đúng thì lệnh timeout
phải kéo dài đúng 15 giây, còn lệnh refused vẫn dừng ở khoảng hai giây.
time curl -4 -o /dev/null --max-time 15 http://10.255.255.1:9999
time curl -4 -o /dev/null --max-time 15 http://127.0.0.1:9999
curl: (28) Connection timed out after 15003 milliseconds
real 0m15.059s
curl: (7) Failed to connect to 127.0.0.1 port 9999 after 2017 ms: …
real 0m2.082s
--max-time tới từng mili-giây; refused không đổi.Kết quả khớp dự đoán. Còn nếu trên máy bạn lệnh đầu dừng sớm với No route to host thì mạng của bạn
đang chủ động từ chối đường tới dải 10.x. Đó là một lời từ chối chứ không phải im lặng, và thấy vậy thì
ghi vào notes.md của bạn.
Lab 3: tự gây lỗi: app listen sai địa chỉ
Lab này dựng lại Hình 6 trên máy bạn. lab/nghe.js là một server nhỏ viết
bằng JavaScript, listen port 8080 trên địa chỉ bạn truyền vào. Bạn chạy nó bằng Node, chương trình chạy JavaScript
ngoài trình duyệt.
Trước hết tìm địa chỉ của máy trong mạng WiFi bằng lệnh ipconfig của Windows, ở dòng
IPv4 Address dưới mục card WiFi. Rồi mở hai cửa sổ Git Bash, đứng ở thư mục của bài.
# Cửa sổ 1: chỉ listen trên loopback
node lab/nghe.js 127.0.0.1
# Cửa sổ 2: thay 192.168.x.y bằng địa chỉ WiFi của máy bạn
netstat -ano | grep ":8080 " | grep LISTEN
curl http://127.0.0.1:8080
curl http://192.168.x.y:8080
Kỳ vọng thấy ba thứ:
netstatin ra127.0.0.1:8080 … LISTENING.- Gọi bằng
127.0.0.1thì nhậnxin chao tu 127.0.0.1. - Gọi bằng địa chỉ WiFi thì ra
curl: (7), làrefused.
App có hỏng không? Không, nó vẫn chạy bình thường, chỉ là nó không nhận kết nối gọi tới địa chỉ WiFi. Đây là thiếu điều kiện có chương trình listen đúng địa chỉ, vì có chương trình listen port 8080 nhưng không phải trên địa chỉ được gọi.
Giờ sửa lỗi vừa gây. Bấm Ctrl+C ở cửa sổ 1, chạy lại với node lab/nghe.js 0.0.0.0, rồi lặp lại ba
lệnh ở cửa sổ 2. Dòng netstat đổi thành 0.0.0.0:8080, và cả hai lệnh curl đều
có câu trả lời.
Bước cuối cần một máy thứ hai hoặc điện thoại cùng WiFi. Từ máy đó mở http://192.168.x.y:8080. Nếu
nó quay một lúc rồi báo lỗi thì bạn đang thiếu điều kiện firewall cho qua, vì firewall của Windows vứt lặng lẽ kết
nối đi vào.
Thêm luật cho port 8080 bằng lệnh ở tab Firewall rồi thử lại. Xong lab thì gỡ luật đi:
Remove-NetFirewallRule -DisplayName "Spring Boot dev 8080"
Gói tin từ máy bạn gửi tới chính địa chỉ WiFi của máy bạn thì không thật sự ra mạng. Hệ điều hành chuyển nó ngay trong máy, và firewall thường không coi đó là kết nối đi vào từ bên ngoài.
Nên hai lệnh curl ở cửa sổ 2 chỉ kiểm được app có listen đúng địa chỉ hay không, không nói gì về
firewall. Muốn kiểm firewall thì gói tin phải thật sự đến từ một máy khác.
Lab 4 (tự chọn): gọi từ mạng khác qua VPN
Lab này cần một dịch vụ VPN cài trên hai máy nên mình để tự chọn, và mình chưa chạy nó trên máy mình. Máy thứ hai dễ kiếm nhất là điện thoại đã tắt WiFi và dùng mạng di động, vì lúc đó nó ở một mạng khác với laptop.
- Cài cùng một phần mềm VPN lên laptop và điện thoại, đăng nhập cùng một tài khoản. Chạy
ipconfigtrên laptop sẽ thấy thêm một mục cho card ảo, địa chỉ ở mục đó là địa chỉ VPN của laptop. - Chạy
node lab/nghe.js 0.0.0.0trên laptop, rồi từ điện thoại mởhttp://<địa chỉ VPN>:8080. Kỳ vọng nhận được câu chào của server. Nếu nó quay một lúc rồi báo lỗi thì firewall trên laptop đang vứt kết nối đi vào, thêm luật như ở tab Firewall. - Tự gây lỗi: dừng server, chạy lại với
node lab/nghe.js 127.0.0.1, rồi gọi lại từ điện thoại. Kỳ vọng bị từ chối ngay, đúng ôrefusedở dòng đầu của Bảng 2. - Chạy lại server với
0.0.0.0, thoát phần mềm VPN trên điện thoại, rồi gọi lại. Kỳ vọng nó quay cho tới khi hết thời hạn chờ, tứctimeout.
Tự kiểm tra
Bấm vào từng dòng khi bạn tự giải thích được, bằng lời của mình, mà không nhìn lại tài liệu.
- Giải thích được vì sao
localhostlà một từ tương đối - Nói được một máy có mấy địa chỉ IP, và vì sao chúng không thay thế được nhau
- Kể được ba điều kiện để gọi tới được một app, theo đúng thứ tự gói tin gặp chúng
- Giải thích được listen
127.0.0.1khác listen0.0.0.0thế nào - Phân biệt được
refusedvàtimeout, và điều kiện nào thiếu sinh ra cái nào - Giải thích được vì sao
refusedchỉ xảy ra khi máy đích còn sống - Đọc được bộ bốn của một kết nối trong output
curl -v - Nói được vì sao một server không đặt trên laptop ở nhà
- Kể được năm bước của một gói tin đi qua VPN, và vì sao VPN không quyết định app listen ở đâu
Những chỗ mình từng hiểu sai
Mỗi dòng dưới đây là một chỗ mình đã hiểu sai khi học bước mở kết nối. Cách nghĩ nào nghe cũng hợp lý, nhưng sai ở một chỗ. Bạn đang nghĩ giống cột đầu thì link ở cột cuối dẫn về đúng mục đã giảng.
| Mình từng nghĩ | Thực tế | Xem lại |
|---|---|---|
127.0.0.1 là địa chỉ IPv4 mặc định của máy | Đó là địa chỉ loopback, một trong nhiều địa chỉ của máy, và chỉ gọi tới được từ trong máy | Một máy có nhiều địa chỉ |
Dải 127.0.0.0/8 có 127⁴ địa chỉ | /8 đếm bit: khối có 2²⁴, khoảng 16,7 triệu địa chỉ, dù máy thường chỉ dùng 127.0.0.1 | Lịch sử địa chỉ IP, tab CIDR |
| "Chưa mở cổng 8080" là một khái niệm | Đó là hai khái niệm khác nhau: có chương trình listen hay không, và firewall có cho qua hay không. Hai thứ cho hai triệu chứng khác nhau | Ba điều kiện |
Server còn sống thì không thể bị refused | Ngược lại: refused chỉ xảy ra khi máy còn sống, vì phải có hệ điều hành đang chạy mới gửi được gói RST | Refused |
| Mở firewall xong là người khác vào được | Chưa chắc. Nếu app chỉ listen 127.0.0.1, triệu chứng chỉ đổi từ timeout sang refused; triệu chứng đổi nghĩa là firewall đã sửa đúng | Ba điều kiện |
| Refused thì nhanh, timeout thì chậm | Trên Windows, refused cũng mất hơn hai giây. Phân biệt bằng việc ai quyết định lúc dừng | Người quyết định dừng |
| Bật VPN thì app tự listen ở địa chỉ của VPN | App chọn địa chỉ listen một lần lúc khởi động. VPN chỉ thêm cho máy một địa chỉ, app listen 0.0.0.0 thì nhận luôn ở địa chỉ đó, còn listen địa chỉ khác thì không | Khác mạng thì sao |
Không ai listen thì người gọi thấy timeout | Không ai listen mà gói tin tới được máy thì hệ điều hành từ chối ngay, tức refused. timeout là khi không có câu trả lời nào | Refused |
Kết lại
Câu hỏi của bài là app chạy bình thường khi gọi bằng localhost:8080 trên chính máy mình mà người
khác gọi vào thì không được, vậy bước mở kết nối hỏng ở đâu? Nó hỏng ở một trong ba điều kiện. Muốn gọi tới được thì
cần đủ cả ba, theo đúng thứ tự gói tin gặp chúng.
- Gọi đúng địa chỉ: một địa chỉ của máy đích mà người gọi với tới được, không phải
localhost. - Firewall cho qua.
- Có chương trình listen đúng địa chỉ: listen port đó trên đúng địa chỉ được gọi tới.
Làm sao biết đang thiếu điều kiện nào? Nhìn triệu chứng. refused là gói tin đã tới một máy đang sống
mà không ai listen ở đó. timeout là không có câu trả lời nào, thường vì firewall vứt gói tin lặng lẽ.
Người gọi ở mạng khác thì cần thêm một đường tới được máy bạn, ví dụ VPN, rồi ba điều kiện vẫn áp dụng y nguyên.
Đủ ba điều kiện thì bắt tay ba bước thành công, kết nối mở, và refused với
timeout biến mất. Nhưng có kết nối chưa có nghĩa là có câu trả lời đúng. Trình duyệt giờ nhận
được một trang, mà trang đó có thể là trang lỗi mang một con số. Từ đó còn lại một câu hỏi:
Kết nối tới được rồi mà trang vẫn báo 502, 500 hay 504. Lỗi đó do ai viết ra, và nó cho biết chặng nào đang hỏng?
Nguồn đọc thêm
Chỉ gồm văn bản chuẩn gốc và tài liệu chính thức. Dùng để tra cứu, không cần đọc hết.
- RFC 791, Internet Protocol. Định nghĩa IPv4 và địa chỉ 32 bit. rfc-editor.org/rfc/rfc791
- RFC 9293, Transmission Control Protocol (TCP). Phần đầu gói TCP, bắt tay ba bước, gói reset. rfc-editor.org/rfc/rfc9293
- RFC 1122, Requirements for Internet Hosts. Dải loopback
127.0.0.0/8. rfc-editor.org/rfc/rfc1122 - RFC 1918, Address Allocation for Private Internets. Ba dải mạng riêng. rfc-editor.org/rfc/rfc1918
- RFC 4291, IP Version 6 Addressing Architecture.
::1vàfe80::/10. rfc-editor.org/rfc/rfc4291 - RFC 6761, Special-Use Domain Names. Tên
localhost. rfc-editor.org/rfc/rfc6761 - RFC 6335, quản lý số hiệu port, gồm khoảng port tạm 49152-65535. rfc-editor.org/rfc/rfc6335
- RFC 2131, Dynamic Host Configuration Protocol. Router cho thuê địa chỉ. rfc-editor.org/rfc/rfc2131
- RFC 4949, Internet Security Glossary, Version 2 (2007). Định nghĩa VPN. rfc-editor.org/rfc/rfc4949
- IANA, sổ đăng ký các dải địa chỉ IPv4 dùng cho mục đích đặc biệt. iana.org/assignments/iana-ipv4-special-registry
- curl, danh sách mã lỗi của libcurl, gồm
CURLE_COULDNT_CONNECT(7) vàCURLE_OPERATION_TIMEDOUT(28). curl.se/libcurl/c/libcurl-errors.html - Microsoft Learn, lệnh
netstat. learn.microsoft.com/…/netstat - Microsoft Learn, lệnh PowerShell
New-NetFirewallRule, gồm tham số-RemoteAddress. learn.microsoft.com/…/new-netfirewallrule
Nguồn ảnh
Ảnh chụp lấy từ Wikimedia Commons, dùng theo giấy phép ghi ở từng dòng; ảnh đã được chuyển sang đen trắng và cắt khung cho hợp bố cục trang; ảnh chụp đồ vật còn được tách khỏi nền cũ để đặt thẳng lên nền trang. Ảnh chụp màn hình do mình tự chụp; các khung màu là phần đánh dấu thêm vào. Truyện tranh của xkcd và của turnoff.us dùng theo giấy phép phi thương mại ghi ở từng dòng; đã đổi cỡ và chuyển sang đen trắng.
- Ảnh 1Networking Problems, Randall Munroe, xkcd.com, CC BY-NC 2.5.
- Ảnh 2Card mạng Intel X540-T2, Dmitry Nosachev, Wikimedia Commons, CC BY-SA 4.0.
- Ảnh 3Sổ đăng ký không gian địa chỉ IPv4, chụp màn hình iana.org, 30/09/2026; đã ẩn các khối 015/8 tới 123/8 và ba cột WHOIS, RDAP, Note.
- Ảnh 4Khuôn phần đầu gói tin TCP, RFC 9293, chụp màn hình rfc-editor.org, 03/10/2026.
- Ảnh 5Firewall Netgear FVS336G, mặt trước, Zuzu, Wikimedia Commons, CC BY-SA 3.0.
- Ảnh 6Bắt tay ba bước, RFC 9293, Hình 6, chụp màn hình rfc-editor.org, 03/10/2026.
- Ảnh 7TCP Buddies, Daniel Stori, turnoff.us, CC BY-NC-SA 4.0.