ICE thất bại là nguyên nhân phổ biến nhất khiến WebRTC không kết nối được. Bài này giải thích tại sao, và cách giải quyết bằng TURN server tự deploy.
Tại Sao ICE Thất Bại?
Trong bài 01 đã giải thích ICE thử các loại candidate theo thứ tự ưu tiên. Vấn đề nằm ở loại NAT của router.
Các Loại NAT
| NAT Type | Mô Tả | P2P Khả Thi? |
|---|---|---|
| Full Cone | Bất kỳ ai biết IP:port đều kết nối được | ✅ Có |
| Restricted Cone | Chỉ IP đã từng liên lạc mới kết nối được | ✅ Thường được |
| Port Restricted | Chỉ IP:port đã từng liên lạc | ✅ Thường được |
| Symmetric | Mỗi lần gửi ra IP:port khác nhau → STUN vô dụng | ❌ Không thể P2P |
Router nhà dân ở Việt Nam (TP-Link, ASUS) phần lớn là Symmetric NAT sau khi 4G/cáp quang. ISP (Viettel, VNPT, FPT) thêm một lớp NAT nữa (CGNAT) → hai lớp Symmetric NAT → STUN hoàn toàn không hoạt động.
Triệu Chứng
I (xxxxx) webrtc: ICE gathering complete
I (xxxxx) webrtc: ICE checking... (30 giây)
E (xxxxx) webrtc: ICE connection failed
Lỗi này xảy ra khi tất cả direct candidates đều fail và không có TURN server.
📷 [Hình minh hoạ: Diagram: Symmetric NAT — ESP32 gửi ra port 1234, nhưng STUN server thấy port 5678 khác với port 9012 khi gửi đến peer]
STUN: Giải Thích Tại Sao Không Đủ
STUN cho ESP32 biết IP:port public của nó — nhưng với Symmetric NAT, ESP32 gửi packet đến peer bằng một port khác với port đã báo cho STUN. Peer nhận được IP:port từ ICE candidate nhưng gửi lại thì bị NAT drop.
Kết quả: hai bên đều biết IP của nhau nhưng không ai nghe thấy ai → ICE fail.
TURN: Giải Pháp Relay
TURN server đứng ở internet, nhận traffic từ cả hai phía và relay qua:
ESP32 → TURN Relay ← Browser
ESP32 ← TURN Relay → Browser
Không cần P2P — traffic đi qua server. Nhược điểm: latency tăng thêm RTT đến TURN server. Ưu điểm: kết nối được 100%.
Deploy coturn (TURN Server Mã Nguồn Mở)
coturn là TURN/STUN server phổ biến nhất, chạy tốt trên VPS giá rẻ (5 USD/tháng đủ dùng).
Cài Trên Ubuntu 22.04
sudo apt update
sudo apt install -y coturn
# Cấu hình
sudo nano /etc/turnserver.conf
Nội dung /etc/turnserver.conf:
# Listening
listening-port=3478
tls-listening-port=5349
# IP public của VPS
external-ip=VPS_PUBLIC_IP
# Auth
lt-cred-mech
user=esp32:password123 # Đổi thành user/pass thực
# Realm
realm=iotlabs.vn
# Log
log-file=/var/log/coturn/turnserver.log
verbose
# Bật service
sudo systemctl enable coturn
sudo systemctl start coturn
# Kiểm tra
sudo systemctl status coturn
Mở Firewall
# UDP ports cho TURN relay
sudo ufw allow 3478/udp
sudo ufw allow 3478/tcp
sudo ufw allow 5349/tcp
sudo ufw allow 49152:65535/udp # TURN relay ports
Test TURN Server
Test bằng WebRTC Trickle ICE tool (trang web public) hoặc bằng turnutils_uclient:
turnutils_uclient -u esp32 -w password123 -p 3478 VPS_PUBLIC_IP
# Kết quả thành công: "Allocated relay: ..."
Cấu Hình TURN Trong esp-webrtc
esp_webrtc_ice_server_t ice_servers[] = {
{
// STUN server
.url = "stun:stun.l.google.com:19302",
},
{
// TURN server
.url = "turn:VPS_PUBLIC_IP:3478",
.username = "esp32",
.credential = "password123",
},
};
esp_webrtc_cfg_t cfg = {
.ice_servers = ice_servers,
.ice_server_num = 2,
// ...
};
Ưu Tiên ICE Candidates
WebRTC tự động ưu tiên P2P trước relay. Chỉ dùng TURN khi P2P thất bại. Thứ tự thử:
1. Host candidates (IP private local) — 0ms overhead
2. Server reflexive (qua STUN) — vài ms overhead
3. Relay (qua TURN) — 20-100ms overhead tùy vị trí VPS
Bạn không cần can thiệp — ICE agent trong esp-webrtc tự xử lý.
TURN Server Nên Đặt Ở Đâu?
Để latency thấp nhất, TURN server nên đặt gần ESP32 và browser:
| Trường Hợp | Đề Xuất |
|---|---|
| ESP32 + User cùng Việt Nam | VPS Singapore (DigitalOcean, Vultr) |
| User khắp nơi | VPS Singapore + Frankfurt |
| Test local | Chạy coturn trên máy tính trong cùng mạng LAN |
Latency từ Việt Nam đến Singapore ~20–40ms — với TURN relay thêm 2×RTT = 40–80ms cho mỗi packet. Acceptable cho video call.
Security: Không Dùng Anonymous TURN
Không cấu hình TURN cho phép anonymous access — ai cũng dùng relay của bạn được, tốn bandwidth:
# SAI — không làm
no-auth
# ĐÚNG — bắt buộc auth
lt-cred-mech
user=USERNAME:PASSWORD
Dùng time-limited credentials cho production (coturn hỗ trợ HMAC-based temporary credentials).
Bài tiếp theo: Dự Án Video Doorbell — Chuông Cửa Video Với ESP32-P4


