Bạn có một IP công cộng và cả chục thiết bị nằm sau router. Chiếc iPad của bạn đang dùng mạng di động, cách xa ba lớp mạng. Vậy mà bằng cách nào đó, một ứng dụng remote desktop mở được kết nối trực tiếp từ đầu này tới đầu kia trong chưa đầy một giây — thường không có relay nào ở giữa. Đây là cách phép màu đó thực sự vận hành: ICE, STUN, TURN, hole punching, và sự thật khó nói về symmetric NAT.
Vì sao điều này quan trọng
Hãy hình dung một sáng thứ Ba. Chiếc Mac mini của bạn ở nhà, nằm sau một router với duy nhất một địa chỉ IPv4 công cộng. Chiếc iPad của bạn đang trên tàu, dùng mạng di động của nhà mạng với các lớp dịch địa chỉ riêng của nó. Có ít nhất hai lớp NAT nằm giữa hai thiết bị, có lẽ là ba. Theo mặc định, không máy nào tiếp cận được từ Internet bên ngoài.
Vậy mà bạn mở Remio trên iPad, chạm vào chiếc Mac, và một giây sau bạn đã nhìn thấy desktop của mình. Hình ảnh và thao tác nhập chảy trực tiếp giữa hai thiết bị — không có cloud relay, không có proxy nào ở giữa mã hoá lại màn hình của bạn.
Kỹ thuật giúp điều này khả thi có một cái tên — NAT traversal — và cả một họ giao thức được định nghĩa rõ ràng đứng sau nó: STUN, TURN và ICE. Đây chính là những giao thức mà WebRTC dùng cho các cuộc gọi video trên trình duyệt, và là lý do bất kỳ remote desktop P2P hiện đại nào cũng tránh được việc phải đẩy các khung hình màn hình của bạn qua trung tâm dữ liệu của bên thứ ba. Bài viết này đi qua từng giao thức làm gì, vì sao nó tồn tại, và cách chúng kết hợp để tìm ra đường có độ trễ thấp nhất giữa hai thiết bị đang ẩn sau router.
Vấn đề NAT trong 60 giây
Internet được thiết kế để mỗi thiết bị có địa chỉ riêng, định tuyến được công khai. Toàn bộ không gian IPv4 có khoảng 4.3 tỷ địa chỉ — dư dả vào năm 1981, và rõ ràng là không đủ vào cuối thập niên 1990.
Giải pháp chắp vá là Network Address Translation. Router của bạn nhận một địa chỉ IPv4 công cộng từ ISP. Bên trong nhà, mỗi thiết bị nhận một địa chỉ riêng từ dải được dành riêng (thường là 192.168.x.x hoặc 10.x.x.x). Khi laptop của bạn gửi một gói tin ra ngoài, router viết lại địa chỉ nguồn thành IP công cộng của chính nó, ghi nhớ ánh xạ đó, rồi chuyển tiếp. Khi phản hồi quay về, router tra ánh xạ và viết lại địa chỉ đích trở lại laptop của bạn.
Đây chính xác là lý do các kết nối đến không hoạt động theo mặc định. Từ bên ngoài, laptop của bạn không có địa chỉ; chỉ router mới có. Một gói tin của người lạ gửi tới your-router-public-ip:5060 chạm vào một router không có ánh xạ nào cho nó và bị loại bỏ.
Với một ứng dụng P2P, đây là toàn bộ cuộc chơi. Cả hai peer đều nằm sau những router chỉ chuyển tiếp gói tin khi chúng nhớ là đã gửi thứ gì đó ra ngoài trước. Không bên nào có thể đơn giản “gọi” cho bên kia — chúng phải hợp tác với router của mình để mở một đường đi tạm thời.
Hole punching: khiến firewall cho bạn quay vào
Đây là mẹo. Bảng NAT của router được lập chỉ mục theo một bộ năm (five-tuple): IP nguồn, cổng nguồn, IP đích, cổng đích và giao thức. Khi thiết bị của bạn gửi một gói UDP ra ngoài tới 1.2.3.4:5678, router tạo một mục tạm thời nói rằng “nếu có bất kỳ gói UDP nào quay về từ 1.2.3.4:5678 tới IP công cộng của tôi ở cổng X, hãy chuyển tiếp nó tới thiết bị này ở cổng Y.”
Mục đó tồn tại trong một khoảng thời gian — thường là 30 đến 60 giây — rồi hết hạn trừ khi có gì đó làm mới nó. Trong khoảng thời gian ấy, mọi gói tin từ địa chỉ đích sẽ được chấp nhận.
Đây chính là lỗ khoá mà các ứng dụng P2P tận dụng. Nếu cả hai peer biết địa chỉ công khai của nhau, mỗi bên có thể gửi một gói UDP ra ngoài tới bên kia cùng một lúc. Router của mỗi bên mở một ánh xạ đi ra. Gói đầu tiên của mỗi bên rất có thể bị loại bỏ — router bên kia chưa mở ánh xạ của nó — nhưng gói thứ hai đã có chỗ để đến. Từ khoảnh khắc đó, cả hai router đều nghĩ chúng đang chuyển tiếp một cuộc trao đổi đi-ra bình thường, và hai thiết bị đang trò chuyện trực tiếp với nhau.
Kỹ thuật này gọi là UDP hole punching. Nó hơi kỳ diệu, được tài liệu hoá kỹ (RFC 5128), và hoạt động với đại đa số NAT gia dụng ngoài thực tế. Những trường hợp còn lại — symmetric NAT, CGNAT, firewall doanh nghiệp — chính là lý do TURN tồn tại.
Hole punching không phải là một mánh khoé. Nó là bản giao ước rõ ràng mà NAT được thiết kế xoay quanh: gói đi-ra tạo ra quyền cho gói đi-vào. Các ứng dụng P2P chỉ đơn giản là chủ động gửi gói đi-ra.
STUN: “địa chỉ công khai của tôi là gì?”
Hole punching cần một điều kiện tiên quyết: mỗi peer phải biết địa chỉ công khai của chính mình để có thể nói cho bên kia biết gửi gói tin về đâu. Địa chỉ đó không nhìn thấy được từ bên trong thiết bị. Chiếc iPad của bạn thấy địa chỉ Wi-Fi nội bộ của nó (192.168.1.42). Còn thứ mà phần còn lại của Internet thấy sau khi router viết lại gói tin thì hoàn toàn khác.
STUN — Session Traversal Utilities for NAT, RFC 5389 — là giao thức tí hon giải quyết điều này. Quy trình đơn giản đến mức gần như đáng ngượng:
- Peer gửi một gói UDP tới một STUN server công cộng. (Remio dùng cả STUN server công cộng của Cloudflare lẫn Google để dự phòng.)
- STUN server đọc IP và cổng nguồn ngay trên đường truyền — theo định nghĩa, đó chính là địa chỉ công khai đã được NAT dịch của peer.
- STUN server trả lời với địa chỉ đó làm payload: “Theo những gì tôi thấy, bạn là
203.0.113.42:48291.” - Giờ peer đã biết địa chỉ nào cần thông báo cho bên kia.
Địa chỉ công khai đó được gọi là server-reflexive candidate trong thuật ngữ ICE. Nó gần như không tốn gì — một vòng khứ hồi trong lúc thiết lập kết nối, và không thêm chút băng thông hay độ trễ nào sau khi kết nối đã được thiết lập. Việc duy nhất của STUN là giới thiệu hai peer với nhau; một khi chúng đã trò chuyện, STUN server không còn vai trò gì nữa.
TURN: khi hole punching thất bại
Hole punching hoạt động với hầu hết NAT, nhưng không phải tất cả. Các trường hợp thất bại là có thật, và một ứng dụng P2P giả vờ chúng không tồn tại thì đơn giản là sẽ không kết nối được cho một phần đáng kể người dùng.
Những thủ phạm chính:
- Symmetric NAT. Một loại NAT gán một cổng công cộng khác nhau cho mỗi đích khác nhau. STUN nói với peer của bạn rằng bạn đang ở cổng 48291 (vì đó là cổng STUN server thấy), nhưng khi bạn gửi một gói tin tới địa chỉ của peer, router của bạn lại chọn một cổng khác — chẳng hạn 48317. Router của peer từ chối gói tin vì nó đến từ một cổng ngoài dự kiến. Hole punching đổ vỡ.
- Carrier-grade NAT (CGNAT). Mạng di động và nhiều mạng ISP đặt thêm một lớp NAT thứ hai ở cấp nhà mạng. Bạn có thể đang nằm sau hai lớp NAT mà không hề hay biết, và cả hai đều phải hợp tác. CGNAT thường là symmetric, và bạn không có quyền quản trị đối với nó.
- Firewall doanh nghiệp hạn chế. Một số mạng doanh nghiệp chặn toàn bộ UDP đi ra, hoặc chỉ cho phép nó tới các đích cụ thể. Hole punching cần một đường UDP; nếu không có, nó không có cơ hội nào.
Với những trường hợp này, phương án dự phòng là TURN — Traversal Using Relays around NAT, RFC 5766. TURN server là một relay mà cả hai peer đều tiếp cận được vì nó có một địa chỉ công cộng luôn chấp nhận lưu lượng đi vào. Mỗi peer tạo một kết nối TLS đi ra bình thường tới TURN server (mà mọi NAT đều cho phép), và server chuyển tiếp các byte giữa hai bên.
TURN là một cái giá thực sự. Mỗi gói video phải đi thêm một chặng mạng. Trên một TURN relay khu vực, điều đó thêm khoảng 30 đến 80 mili-giây độ trễ khứ hồi. Xuyên vùng — chẳng hạn một relay ở Frankfurt cho một peer ở Tokyo và một peer ở São Paulo — có thể là 80 đến 200 mili-giây. Đơn vị vận hành TURN cũng phải trả tiền băng thông: từng bit trong luồng của bạn đều đi qua đường ống của họ.
Đây là lý do một ứng dụng thiết kế tốt chỉ dùng TURN khi hole punching đã thất bại một cách rõ ràng. ICE tự động lo quyết định đó.
ICE: thử mọi ứng viên song song
Interactive Connectivity Establishment, RFC 8445, là màn dàn dựng gắn kết tất cả lại với nhau. ICE không chọn sẵn một chiến lược từ đầu. Nó thu thập mọi địa chỉ ứng viên khả dĩ cho từng peer, trao đổi toàn bộ danh sách với phía bên kia, rồi để cả hai stack dò từng cặp ứng viên song song — chạy đua để tìm ra cặp đầu tiên hoạt động.
Với mỗi peer, ICE thường thu thập:
- Host candidate — mọi địa chỉ IP nội bộ trên mọi giao diện mạng (Ethernet, Wi-Fi, giao diện ảo, IPv4 và IPv6).
- Server-reflexive candidate — các địa chỉ công khai học được từ STUN server.
- Relay candidate — các địa chỉ được cấp phát trên một TURN server.
Hai peer trao đổi các danh sách này qua một kênh signaling — với Remio, đó là một kết nối WebSocket nhỏ tới relay.remio.net, mang theo các ICE candidate và mô tả phiên WebRTC (SDP), rồi không còn việc gì để làm một khi media bắt đầu chảy.
Một khi đã có cả hai danh sách, ICE bắt đầu kiểm tra kết nối (connectivity check). Mỗi peer gửi một gói STUN-binding-request dọc theo từng cặp ứng viên, theo thứ tự ưu tiên. Các mức ưu tiên được thiết kế để ưu ái đường có độ trễ thấp nhất:
- Host tới host — cả hai peer trên cùng một mạng nội bộ. Đây là trường hợp trong mơ: độ trễ vài trăm micro-giây, không có router nào tham gia.
- Host tới server-reflexive, hoặc server-reflexive tới server-reflexive — các peer trên những mạng khác nhau, được hole punch xuyên qua NAT tương ứng của chúng. Đây là ý nghĩa thực tế thường thấy của cụm “P2P qua Internet”.
- Bất cứ đường nào có relay — biện pháp cuối cùng, chỉ dùng khi mọi đường ưu tiên cao hơn đều trượt kiểm tra kết nối.
Cặp ứng viên đầu tiên mà cả hai chiều đều thành công được đề cử làm bên thắng cuộc, và media bắt đầu chảy dọc theo đường đó. Toàn bộ quá trình thường hoàn tất trong chưa đầy một giây.
ICE cũng thu thập ứng viên một cách liên tục. Nếu một đường tốt hơn xuất hiện giữa phiên — chẳng hạn điện thoại của bạn rời mạng di động và vào Wi-Fi — ICE có thể đề cử lại sang đường mới mà không làm rớt phiên.
Vì sao Remio quyết ưu tiên P2P
Remio là một ứng dụng remote desktop, không phải cuộc gọi video. Ngân sách độ trễ khắc nghiệt vô cùng. Để một phiên cho cảm giác như bạn đang ngồi ngay tại máy host, toàn bộ pipeline — capture, encode, mạng, decode, render — phải gói gọn trong khoảng 16 mili-giây (một khung hình ở 60 FPS). Mỗi chặng dư thừa là một tội lỗi.
Trên một kết nối P2P LAN trực tiếp, phần mạng trong ngân sách đó chỉ khoảng 1 đến 5 mili-giây khứ hồi. Không có relay; các byte đi thẳng từ thiết bị này sang thiết bị kia. Với pipeline của Remio, điều này cho phép chúng tôi đạt độ trễ glass-to-glass dưới 5 ms từ đầu tới cuối. Con số đó nằm dưới ngưỡng con người có thể cảm nhận — chiếc máy từ xa cho cảm giác không thể phân biệt với một chiếc máy tại chỗ.
Trên một kết nối qua TURN relay — kể cả relay khu vực — phần mạng nhảy lên 30 đến 80 mili-giây. Tổng độ trễ người dùng cảm nhận lúc này là 40 đến 100 ms. Vẫn dùng được cho hầu hết công việc, nhưng bạn có thể cảm nhận được khi kéo nhanh hoặc bấm liên tiếp. Trackpad không còn thật sự giống của bạn nữa.
Chính khoảng chênh lệch đó là lý do chúng tôi dày công giữ các phiên chạy trên P2P. Stack WebRTC của Remio thu thập cả ứng viên IPv4 lẫn IPv6, trò chuyện với nhiều STUN server song song, và ưu tiên quyết liệt các đường trực tiếp trong lúc ICE chọn cặp ứng viên. Trong nội bộ, chúng tôi thậm chí dùng RTT kết nối đo được để phân loại mạng — dưới 15 ms là chúng tôi biết đó là LAN, đặt encoder ở 8 Mbps CBR với dư địa lên tới 30 Mbps cho keyframe, và mồi bộ ước lượng băng thông ở mức cao. Trên 15 ms được coi là WAN và được tinh chỉnh dè dặt hơn.
Và — đây là phần mà các đối thủ thường lặng lẽ bỏ qua — kết nối được mã hoá đầu-cuối bất kể đi theo đường nào. Khoá AES-256-GCM được thương lượng qua ECDHE trên Curve25519 trực tiếp giữa hai peer. Dù các byte chảy theo P2P hay qua TURN, cả server signaling của Remio lẫn đơn vị vận hành TURN đều không đọc được chúng. P2P chỉ đơn giản nghĩa là hoàn toàn không có bên thứ ba nào trên đường dữ liệu.
Khi relay là không thể tránh (và điều đó ổn)
Đôi khi các vị thần mạng nói không. Symmetric NAT ở cả hai đầu, CGNAT kép trên một nhà mạng di động đi kèm ISP nghiêm ngặt, một firewall doanh nghiệp chỉ cho phép TLS đi ra tới các nhà cung cấp cloud đã biết — đây đều là những cấu hình có thật và phổ biến. Trong những trường hợp đó, phiên đơn giản là buộc phải đi qua relay, nếu không thì chẳng kết nối được chút nào.
Backend TURN của Remio là Cloudflare Calls, mang lại cho chúng tôi một mạng lưới relay toàn cầu gần với hầu hết người dùng. Khi ICE chọn đường relay, trải nghiệm tụt từ “cảm giác như tại chỗ” xuống “cảm giác như Splashtop” — vẫn rất dùng được, nhưng bạn nhận ra được. Độ trễ tăng thêm rơi vào khoảng 30 đến 80 ms với relay khu vực; xuyên vùng có thể đẩy lên 200 ms.
Phần quan trọng: dữ liệu vẫn được mã hoá đầu-cuối. Cloudflare thấy các gói UDP mã hoá đi theo một chiều và các gói UDP mã hoá đi theo chiều kia. Nó có thể đếm byte để tính phí. Nó không thể thấy màn hình, phím gõ hay bất kỳ nội dung phiên nào của bạn. Cũng không có điểm giải mã nào ở phía Remio, bởi vì không có cloud Remio nào trong pipeline — chúng tôi vận hành dịch vụ TURN thông qua Cloudflare và không bao giờ chạm vào payload.
Đây là kiến trúc mà security whitepaper của chúng tôi mô tả chi tiết hơn. Tóm lại: dù P2P hay relay, mô hình mối đe doạ là như nhau.
Lối thoát IPv6
IPv6 có 340 undecillion địa chỉ (3.4 × 1038) — đủ để cấp cho mỗi hạt cát trên Trái Đất một IP công cộng riêng, vẫn còn dư vài nghìn tỷ. Trong một thế giới chỉ có IPv6, NAT phần lớn là không cần thiết. Hai thiết bị hỗ trợ IPv6 chỉ việc gửi gói tin tới địa chỉ của nhau, và chướng ngại duy nhất là firewall trên host ở mỗi đầu.
Nếu bạn có IPv6 ở nhà và trên nhà mạng di động, có thể bạn từng để ý rằng một số ứng dụng kết nối nhanh hơn khi cả hai đầu đều là IPv6 — đó là vì toàn bộ điệu nhảy “thu thập ứng viên, dò các cặp” có ít chướng ngại hơn. Không có việc dịch NAT nào để khám phá, không cần chuyến đi STUN nào, chỉ có các địa chỉ trực tiếp.
Nhưng cái bẫy là: trong năm 2026, quá trình triển khai toàn cầu vẫn còn dang dở. Nhiều router gia đình hỗ trợ IPv6 kém hoặc chẳng hỗ trợ gì cả. Nhiều mạng doanh nghiệp chặn IPv6 đi vào theo mặc định. Một số nhà mạng cung cấp IPv6 ở phía sóng nhưng lại bắc cầu sang IPv4 ở nơi khác. Các đường IPv6-tới-IPv6 thuần tuý có tồn tại nhưng không thể trông cậy như cơ chế duy nhất.
Vì vậy Remio — như mọi stack P2P nghiêm túc — thu thập cả ứng viên IPv4 lẫn IPv6, và để ICE cho chúng chạy đua. Nếu một đường IPv6 trực tiếp tồn tại và hoạt động, nó thường thắng về độ trễ. Nếu không, IPv4 với hole punching sẽ tiếp quản. Nếu cả hai đều thất bại, thì dùng relay. Người dùng không bao giờ thấy bất kỳ điều nào trong số này; kết nối cứ thế được thiết lập.
Cách biết phiên của bạn đang dùng đường nào
Tò mò không biết phiên của bạn cuối cùng chạy trên đường nào? Chỉ báo kết nối của Remio hiển thị loại đường bằng ngôn ngữ dễ hiểu — “Direct” cho P2P, “Relay” cho TURN. Bảng thống kê dành cho lập trình viên phơi bày toàn bộ trạng thái WebRTC, gồm cả loại ứng viên của cặp được đề cử (host, srflx, relay) và RTT đo được.
Vài cách diễn giải nhanh:
- RTT dưới 5 ms, Direct — bạn ở cùng LAN với host. Trường hợp tốt nhất. Có thể đạt glass-to-glass dưới 5 ms.
- RTT 5 đến 15 ms, Direct — cùng mạng Wi-Fi nhưng có thêm một chặng access-point hoặc mesh. Vẫn tuyệt vời.
- RTT 15 đến 80 ms, Direct — bạn ở một mạng khác với host, hole punching đã thành công. P2P qua Internet đang hoạt động.
- RTT 30 đến 80 ms, Relay (khu vực) — symmetric NAT hoặc carrier-grade NAT đã đánh bại hole punch. Cloudflare TURN ở gần đã tiếp quản.
- RTT 80 đến 200 ms, Relay — TURN qua một khoảng cách địa lý xa hơn, hoặc bạn và host ở những vùng rất khác nhau.
Nếu bạn luôn rớt sang relay ngay cả khi ở nhà, những nghi phạm quen thuộc là một modem ISP đang chạy symmetric NAT, IPv4 CGNAT trên đường truyền nhà bạn (phổ biến hơn bạn tưởng ở các đơn vị bán lại cáp quang), hoặc một firewall quá khắt khe chặn UDP đi vào. Hướng dẫn giảm độ trễ remote desktop của chúng tôi đi qua các thủ phạm thường gặp, và trang kết nối LAN trực tiếp là con đường nhanh nếu cả hai thiết bị đều ở trong mạng của bạn.
Số liệu hiệu năng thực tế
Gộp tất cả lại, đây là các số liệu độ trễ chúng tôi đo trong môi trường production trên các đường đi khác nhau mà một phiên Remio có thể chọn:
- P2P LAN trực tiếp: RTT mạng 1–5 ms. Đầu-cuối (từ sự kiện nhập tới cập nhật pixel) dưới 5 ms khi phần còn lại của pipeline phối hợp tốt.
- Wi-Fi cùng mạng có thêm chặng: RTT mạng 5–10 ms.
- TURN relay, khu vực: RTT mạng 30–80 ms.
- TURN relay, xuyên vùng: RTT mạng 80–200 ms.
- Di động 4G/5G, P2P đã hole punch: RTT mạng 40–100 ms.
Những con số này khớp với các số liệu trong benchmark của Remio và giải thích vì sao chúng tôi đặt ngưỡng LAN-vs-WAN ở 15 ms RTT — nó phân tách gọn gàng các đường trực tiếp khỏi mọi đường relay (tối thiểu 30 ms) hoặc đường di động (tối thiểu 40 ms), với rất ít cảnh báo nhầm.
NAT traversal là một trong những chủ đề mà từng giao thức riêng lẻ thì đơn giản nhưng tổ hợp của chúng lại rối rắm. Hầu hết người dùng sẽ không bao giờ cần biết ICE, STUN và TURN tồn tại — họ chỉ trải nghiệm một ứng dụng kết nối nhanh từ bất cứ đâu tới bất cứ đâu. Các giao thức đang làm tốt việc của mình khi bạn không nhận ra chúng. Khi bạn có nhận ra — một phiên mất lâu hơn một nhịp, hoặc “Direct” biến thành “Relay” — thì giờ bạn đã có mô hình tư duy.
Để tìm hiểu thêm về pipeline native của Remio và những lựa chọn biến 1–5 ms độ trễ mạng thành glass-to-glass dưới 5 ms, xem vì sao native quan trọng. Về mô hình mối đe doạ trên các đường relay, xem security whitepaper. Hoặc chỉ cần xem qua các tính năng và tải Remio để cảm nhận nó vận hành.