Truyền tín hiệu là gì?
Truyền tín hiệu là quá trình điều phối hoạt động giao tiếp. Để thiết lập cuộc gọi, ứng dụng WebRTC cần trao đổi thông tin sau với các máy khách:
- Thông báo kiểm soát phiên được dùng để mở hoặc đóng hoạt động giao tiếp
- Thông báo lỗi
- Siêu dữ liệu của nội dung nghe nhìn, chẳng hạn như bộ mã hoá và giải mã, chế độ cài đặt bộ mã hoá và giải mã, băng thông và loại nội dung nghe nhìn
- Dữ liệu khoá được dùng để thiết lập các kết nối an toàn
- Dữ liệu mạng, chẳng hạn như địa chỉ IP và cổng của máy chủ lưu trữ mà thế giới bên ngoài nhìn thấy
Quy trình báo hiệu này cần có cách để các ứng dụng truyền thông điệp qua lại. API WebRTC không triển khai cơ chế đó. Bạn cần tự xây dựng. Trong phần sau của bài viết này, bạn sẽ tìm hiểu các cách để tạo một dịch vụ báo hiệu. Tuy nhiên, trước tiên, bạn cần biết một chút bối cảnh.
Tại sao WebRTC không xác định việc báo hiệu?
Để tránh dư thừa và tối đa hoá khả năng tương thích với các công nghệ đã thiết lập, các phương thức và giao thức báo hiệu không được chỉ định theo tiêu chuẩn WebRTC. Cách tiếp cận này được trình bày trong Giao thức thiết lập phiên JavaScript (JSEP):
Cấu trúc của JSEP cũng giúp trình duyệt không phải lưu trạng thái, tức là hoạt động như một máy trạng thái báo hiệu. Điều này sẽ gây ra vấn đề nếu, chẳng hạn như, dữ liệu báo hiệu bị mất mỗi khi một trang được tải lại. Thay vào đó, trạng thái báo hiệu có thể được lưu trên một máy chủ.

JSEP yêu cầu trao đổi đề nghị và phản hồi giữa các thiết bị ngang hàng, siêu dữ liệu đa phương tiện được đề cập ở trên. Đề nghị và câu trả lời được truyền đạt ở định dạng Giao thức mô tả phiên (SDP), có dạng như sau:
v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
…
Bạn muốn biết tất cả những thuật ngữ khó hiểu này trong SDP thực sự có nghĩa là gì? Hãy xem các ví dụ của Lực lượng chuyên trách kỹ thuật Internet (IETF).
Xin lưu ý rằng WebRTC được thiết kế để có thể điều chỉnh đề nghị hoặc câu trả lời trước khi được đặt làm nội dung mô tả cục bộ hoặc từ xa bằng cách chỉnh sửa các giá trị trong văn bản SDP. Ví dụ: bạn có thể dùng hàm preferAudioCodec()
trong appr.tc để đặt codec và tốc độ bit mặc định. SDP hơi khó thao tác bằng JavaScript và có cuộc thảo luận về việc liệu các phiên bản WebRTC trong tương lai có nên sử dụng JSON hay không, nhưng có một số lợi thế khi sử dụng SDP.
RTCPeerConnection
API và báo hiệu: Lời đề nghị, câu trả lời và ứng cử viên
RTCPeerConnection
là API mà các ứng dụng WebRTC dùng để tạo kết nối giữa các thiết bị ngang hàng và truyền thông tin âm thanh và video.
Để khởi động quy trình này, RTCPeerConnection
có 2 nhiệm vụ:
- Xác định các điều kiện về nội dung nghe nhìn tại địa phương, chẳng hạn như khả năng về độ phân giải và codec. Đây là siêu dữ liệu được dùng cho cơ chế đề nghị và phản hồi.
- Nhận địa chỉ mạng tiềm năng cho máy chủ lưu trữ của ứng dụng, còn gọi là các ứng cử viên.
Sau khi xác định được dữ liệu cục bộ này, bạn phải trao đổi dữ liệu đó thông qua một cơ chế báo hiệu với thiết bị ngang hàng từ xa.
Giả sử Alice đang cố gắng gọi cho Eve. Dưới đây là cơ chế đề nghị/trả lời đầy đủ và chi tiết:
- Alice tạo một đối tượng
RTCPeerConnection
. - Alice tạo một đề nghị (phần mô tả phiên SDP) bằng phương thức
RTCPeerConnection
createOffer()
. - Alice gọi
setLocalDescription()
để đưa ra ưu đãi. - Alice chuyển lời đề nghị thành chuỗi và sử dụng cơ chế báo hiệu để gửi lời đề nghị đó cho Eve.
- Eve gọi
setRemoteDescription()
bằng đề nghị của Alice, đểRTCPeerConnection
của Eve biết về chế độ thiết lập của Alice. - Eve gọi
createAnswer()
và lệnh gọi lại thành công cho lệnh gọi này sẽ được truyền nội dung mô tả phiên cục bộ – câu trả lời của Eve. - Eve đặt câu trả lời của mình làm nội dung mô tả cục bộ bằng cách gọi
setLocalDescription()
. - Sau đó, Eve sử dụng cơ chế báo hiệu để gửi câu trả lời đã chuyển đổi thành chuỗi cho Alice.
- Alice đặt câu trả lời của Eve làm nội dung mô tả phiên từ xa bằng cách sử dụng
setRemoteDescription()
.
Alice và Eve cũng cần trao đổi thông tin mạng. Cụm từ "tìm kiếm các ứng cử viên" đề cập đến quy trình tìm kiếm các giao diện mạng và cổng bằng cách sử dụng khung ICE.
- Alice tạo một đối tượng
RTCPeerConnection
bằng trình xử lýonicecandidate
. - Trình xử lý được gọi khi có các ứng cử viên mạng.
- Trong trình xử lý, Alice gửi dữ liệu đề xuất được chuyển đổi thành chuỗi cho Eve thông qua kênh báo hiệu của họ.
- Khi Eve nhận được một thông báo đề xuất từ Alice, cô ấy sẽ gọi
addIceCandidate()
để thêm đề xuất đó vào nội dung mô tả của thiết bị ngang hàng từ xa.
JSEP hỗ trợ ICE Candidate Trickling (Truyền từng phần ICE Candidate), cho phép người gọi cung cấp dần các candidate cho người nhận cuộc gọi sau lời đề nghị ban đầu, và cho phép người nhận cuộc gọi bắt đầu hành động trong cuộc gọi và thiết lập kết nối mà không cần đợi tất cả các candidate đến.
Mã WebRTC để báo hiệu
Đoạn mã sau đây là một ví dụ về mã W3C tóm tắt quy trình báo hiệu hoàn chỉnh. Mã giả định sự tồn tại của một số cơ chế báo hiệu, SignalingChannel
. Việc báo hiệu sẽ được thảo luận chi tiết hơn ở phần sau.
// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});
// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
// send the offer to the other peer
signaling.send({desc: pc.localDescription});
} catch (err) {
console.error(err);
}
};
// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
// Don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
// Call start() to initiate.
async function start() {
try {
// Get local stream, show it in self-view, and add it to be sent.
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
signaling.onmessage = async ({desc, candidate}) => {
try {
if (desc) {
// If you get an offer, you need to reply with an answer.
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({desc: pc.localDescription});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc);
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
Để xem quy trình đề nghị/trả lời và trao đổi ứng viên đang hoạt động, hãy xem simpl.info RTCPeerConnection và xem nhật ký bảng điều khiển cho ví dụ về cuộc trò chuyện video trên một trang. Nếu bạn muốn biết thêm, hãy tải một bản kết xuất hoàn chỉnh về tín hiệu và số liệu thống kê WebRTC từ trang about://webrtc-internals trong Google Chrome hoặc trang opera://webrtc-internals trong Opera.
Khám phá ngang hàng
Đây là một cách hỏi sang trọng hơn cho câu hỏi "Làm cách nào để tìm người trò chuyện?"
Đối với cuộc gọi điện thoại, bạn có số điện thoại và danh bạ. Đối với tính năng nhắn tin và trò chuyện video trực tuyến, bạn cần có hệ thống quản lý danh tính và trạng thái hiện diện, cũng như phương tiện để người dùng bắt đầu phiên. Các ứng dụng WebRTC cần có cách để các ứng dụng khách báo hiệu cho nhau rằng họ muốn bắt đầu hoặc tham gia cuộc gọi.
WebRTC không xác định các cơ chế khám phá ngang hàng và bạn không cần xem xét các lựa chọn ở đây. Quy trình này có thể đơn giản như gửi email hoặc nhắn tin một URL. Đối với các ứng dụng trò chuyện video, chẳng hạn như Talky, tawk.to và Browser Meeting, bạn mời mọi người tham gia cuộc gọi bằng cách chia sẻ một đường liên kết tuỳ chỉnh. Nhà phát triển Chris Ball đã tạo ra một thử nghiệm serverless-webrtc thú vị, cho phép người tham gia cuộc gọi WebRTC trao đổi siêu dữ liệu bằng bất kỳ dịch vụ nhắn tin nào họ muốn, chẳng hạn như tin nhắn tức thời, email hoặc bồ câu đưa thư.
Làm cách nào để xây dựng một dịch vụ báo hiệu?
Xin nhắc lại rằng các giao thức và cơ chế báo hiệu không được xác định theo tiêu chuẩn WebRTC. Dù chọn cách nào, bạn cũng cần một máy chủ trung gian để trao đổi thông báo báo hiệu và dữ liệu ứng dụng giữa các ứng dụng. Rất tiếc, một ứng dụng web không thể chỉ đơn giản là "Kết nối tôi với bạn bè!" trên Internet.
Rất may là các thông báo báo hiệu có kích thước nhỏ và chủ yếu được trao đổi khi bắt đầu cuộc gọi. Trong quá trình thử nghiệm với appr.tc cho một phiên trò chuyện video, tổng cộng có khoảng 30 đến 45 tin nhắn được dịch vụ báo hiệu xử lý với tổng kích thước cho tất cả tin nhắn là khoảng 10 KB.
Ngoài việc không đòi hỏi nhiều về băng thông, các dịch vụ báo hiệu WebRTC không tiêu tốn nhiều tài nguyên xử lý hoặc bộ nhớ vì chúng chỉ cần chuyển tiếp thông báo và giữ lại một lượng nhỏ dữ liệu trạng thái phiên, chẳng hạn như những máy khách nào được kết nối.
Đẩy thông báo từ máy chủ đến ứng dụng
Dịch vụ nhắn tin để báo hiệu cần phải có hai chiều: từ ứng dụng đến máy chủ và từ máy chủ đến ứng dụng. Giao tiếp hai chiều đi ngược lại mô hình yêu cầu/phản hồi máy chủ/máy khách HTTP, nhưng nhiều giải pháp như truy vấn dài đã được phát triển trong nhiều năm để đẩy dữ liệu từ một dịch vụ đang chạy trên máy chủ web đến một ứng dụng web đang chạy trong trình duyệt.
Gần đây hơn, API EventSource
đã được triển khai rộng rãi. Điều này cho phép các sự kiện do máy chủ gửi – dữ liệu được gửi từ máy chủ web đến ứng dụng trình duyệt thông qua HTTP. EventSource
được thiết kế để truyền thông điệp một chiều, nhưng bạn có thể kết hợp với XHR để tạo một dịch vụ trao đổi thông điệp báo hiệu. Dịch vụ báo hiệu truyền một thông báo từ người gọi, được gửi bằng yêu cầu XHR, bằng cách đẩy thông báo đó qua EventSource
đến người nhận cuộc gọi.
WebSocket là một giải pháp tự nhiên hơn, được thiết kế để giao tiếp hai chiều giữa máy khách và máy chủ – những thông báo có thể truyền theo cả hai hướng cùng một lúc. Một lợi thế của dịch vụ báo hiệu được xây dựng bằng WebSocket thuần tuý hoặc các sự kiện do máy chủ gửi (EventSource
) là phần phụ trợ cho các API này có thể được triển khai trên nhiều khung web phổ biến đối với hầu hết các gói lưu trữ web cho các ngôn ngữ như PHP, Python và Ruby.
Tất cả trình duyệt hiện đại (ngoại trừ Opera Mini) đều hỗ trợ WebSocket và quan trọng hơn là tất cả trình duyệt hỗ trợ WebRTC cũng hỗ trợ WebSocket, cả trên máy tính và thiết bị di động. Bạn nên sử dụng TLS cho tất cả các kết nối để đảm bảo thư không bị chặn khi chưa mã hoá và cũng để giảm các vấn đề khi truyền qua proxy. (Để biết thêm thông tin về WebSocket và việc truyền qua proxy, hãy xem chương WebRTC trong cuốn High Performance Browser Networking của Ilya Grigorik.)
Bạn cũng có thể xử lý việc báo hiệu bằng cách yêu cầu ứng dụng WebRTC liên tục thăm dò một máy chủ nhắn tin thông qua Ajax, nhưng điều đó dẫn đến nhiều yêu cầu mạng dư thừa, đặc biệt là đối với thiết bị di động. Ngay cả sau khi phiên được thiết lập, các thiết bị ngang hàng vẫn cần thăm dò các thông báo báo hiệu trong trường hợp có thay đổi hoặc phiên bị chấm dứt bởi các thiết bị ngang hàng khác. Ví dụ về ứng dụng WebRTC Book sử dụng lựa chọn này cùng với một số điểm tối ưu hoá về tần suất thăm dò.
Báo hiệu theo tỷ lệ
Mặc dù một dịch vụ báo hiệu tiêu thụ tương đối ít băng thông và CPU trên mỗi máy khách, nhưng các máy chủ báo hiệu cho một ứng dụng phổ biến có thể phải xử lý nhiều thông báo từ nhiều vị trí với mức độ đồng thời cao. Các ứng dụng WebRTC có lưu lượng truy cập lớn cần có các máy chủ báo hiệu có khả năng xử lý tải đáng kể. Bạn không cần đi vào chi tiết ở đây, nhưng có một số lựa chọn để gửi tin nhắn với số lượng lớn và hiệu suất cao, bao gồm cả những lựa chọn sau:
Giao thức nhắn tin và trạng thái có thể mở rộng (XMPP), ban đầu được gọi là Jabber – một giao thức được phát triển để nhắn tin tức thời, có thể dùng để báo hiệu (Các phương thức triển khai máy chủ bao gồm ejabberd và Openfire. Các ứng dụng JavaScript, chẳng hạn như Strophe.js, sử dụng BOSH để mô phỏng tính năng truyền trực tuyến hai chiều, nhưng vì nhiều lý do, BOSH có thể không hiệu quả bằng WebSocket và vì những lý do tương tự, BOSH có thể không mở rộng quy mô tốt.) (Nói thêm, Jingle là một tiện ích XMPP để bật tính năng thoại và video. Dự án WebRTC sử dụng các thành phần mạng và truyền tải từ thư viện libjingle (một cách triển khai Jingle bằng C++).
Các thư viện nguồn mở, chẳng hạn như ZeroMQ (do TokBox sử dụng cho dịch vụ Rumour của họ) và OpenMQ (NullMQ áp dụng các khái niệm ZeroMQ cho các nền tảng web bằng giao thức STOMP qua WebSocket).
Các nền tảng nhắn tin thương mại trên đám mây sử dụng WebSocket (mặc dù có thể quay lại chế độ thăm dò ý kiến trong thời gian dài), chẳng hạn như Pusher, Kaazing và PubNub (PubNub cũng có một API cho WebRTC).
Các nền tảng WebRTC thương mại, chẳng hạn như vLine
(Hướng dẫn về công nghệ web theo thời gian thực của nhà phát triển Phil Leggetter cung cấp danh sách đầy đủ các dịch vụ và thư viện nhắn tin.)
Tạo dịch vụ báo hiệu bằng Socket.io trên Node
Sau đây là mã cho một ứng dụng web đơn giản sử dụng dịch vụ báo hiệu được tạo bằng Socket.io trên Node. Thiết kế của Socket.io giúp bạn dễ dàng tạo một dịch vụ để trao đổi thông báo và Socket.io đặc biệt phù hợp với việc báo hiệu WebRTC vì có sẵn khái niệm về phòng. Ví dụ này không được thiết kế để mở rộng quy mô như một dịch vụ báo hiệu cấp sản xuất, nhưng dễ hiểu đối với một số lượng người dùng tương đối nhỏ.
Socket.io sử dụng WebSocket với các phương án dự phòng: AJAX long polling, AJAX multipart streaming, Forever Iframe và JSONP polling. Thư viện này đã được chuyển sang nhiều chương trình phụ trợ, nhưng có lẽ được biết đến nhiều nhất là phiên bản Node được dùng trong ví dụ này.
Trong ví dụ này, không có WebRTC. Ứng dụng này chỉ được thiết kế để minh hoạ cách tạo tín hiệu trong một ứng dụng web. Hãy xem nhật ký bảng điều khiển để biết những gì đang xảy ra khi các ứng dụng tham gia vào một phòng và trao đổi thông báo. Lớp học lập trình WebRTC này cung cấp hướng dẫn từng bước về cách tích hợp WebRTC vào một ứng dụng trò chuyện video WebRTC hoàn chỉnh.
Sau đây là ứng dụng index.html
:
<!DOCTYPE html>
<html>
<head>
<title>WebRTC client</title>
</head>
<body>
<script src='/socket.io/socket.io.js'></script>
<script src='js/main.js'></script>
</body>
</html>
Sau đây là tệp JavaScript main.js
được tham chiếu trong ứng dụng:
const isInitiator;
room = prompt('Enter room name:');
const socket = io.connect();
if (room !== '') {
console.log('Joining room ' + room);
socket.emit('create or join', room);
}
socket.on('full', (room) => {
console.log('Room ' + room + ' is full');
});
socket.on('empty', (room) => {
isInitiator = true;
console.log('Room ' + room + ' is empty');
});
socket.on('join', (room) => {
console.log('Making request to join room ' + room);
console.log('You are the initiator!');
});
socket.on('log', (array) => {
console.log.apply(console, array);
});
Sau đây là ứng dụng máy chủ hoàn chỉnh:
const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);
const io = require('socket.io').listen(app);
io.sockets.on('connection', (socket) => {
// Convenience function to log server messages to the client
function log(){
const array = ['>>> Message from server: '];
for (const i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', (message) => {
log('Got message:', message);
// For a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', (room) => {
const numClients = io.sockets.clients(room).length;
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);
if (numClients === 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id +
' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id +
' joined room ' + room);
});
});
(Bạn không cần tìm hiểu về node-static cho việc này. Đây chỉ là một ví dụ về cách sử dụng.)
Để chạy ứng dụng này trên localhost, bạn cần cài đặt Node, Socket.IO và node-static. Bạn có thể tải Node xuống từ Node.js (quá trình cài đặt rất đơn giản và nhanh chóng). Để cài đặt Socket.IO và node-static, hãy chạy Trình quản lý gói Node từ một thiết bị đầu cuối trong thư mục ứng dụng của bạn:
npm install socket.io
npm install node-static
Để khởi động máy chủ, hãy chạy lệnh sau từ một thiết bị đầu cuối trong thư mục ứng dụng của bạn:
node server.js
Trên trình duyệt, hãy mở localhost:2013
. Mở một thẻ hoặc cửa sổ mới trong trình duyệt bất kỳ rồi mở lại localhost:2013
. Để biết điều gì đang xảy ra, hãy kiểm tra bảng điều khiển. Trong Chrome và Opera, bạn có thể truy cập vào bảng điều khiển thông qua Công cụ dành cho nhà phát triển của Google Chrome bằng cách nhấn Ctrl+Shift+J
(hoặc Command+Option+J
trên máy Mac).
Dù bạn chọn phương pháp nào để báo hiệu, thì ít nhất, ứng dụng phụ trợ và ứng dụng khách của bạn cũng cần cung cấp các dịch vụ tương tự như ví dụ này.
Các lỗi thường gặp về báo hiệu
RTCPeerConnection
sẽ không bắt đầu thu thập các đề xuất cho đến khisetLocalDescription()
được gọi. Điều này là bắt buộc trong bản nháp JSEP IETF.- Tận dụng Trickle ICE. Gọi cho
addIceCandidate()
ngay khi ứng viên đến.
Máy chủ báo hiệu được tạo sẵn
Nếu không muốn tự triển khai, bạn có thể sử dụng một số máy chủ báo hiệu WebRTC. Các máy chủ này sử dụng Socket.IO như ví dụ trước và được tích hợp với các thư viện JavaScript của ứng dụng WebRTC:
- webRTC.io là một trong những thư viện trừu tượng đầu tiên cho WebRTC.
- Signalmaster là một máy chủ báo hiệu được tạo để sử dụng với thư viện ứng dụng JavaScript SimpleWebRTC.
Nếu bạn không muốn viết bất kỳ mã nào, thì các nền tảng WebRTC thương mại hoàn chỉnh có sẵn từ các công ty như vLine, OpenTok và Asterisk.
Ericsson đã tạo một máy chủ báo hiệu bằng PHP trên Apache trong những ngày đầu của WebRTC. Giờ đây, mã này đã lỗi thời, nhưng bạn nên xem xét mã này nếu đang cân nhắc một giải pháp tương tự.
Bảo mật báo hiệu
"Bảo mật là nghệ thuật ngăn chặn mọi sự cố."
Salman Rushdie
Mã hoá là bắt buộc đối với tất cả các thành phần WebRTC.
Tuy nhiên, các tiêu chuẩn WebRTC không xác định cơ chế báo hiệu, vì vậy, bạn phải đảm bảo việc báo hiệu được bảo mật. Nếu tìm cách xâm nhập được vào quá trình báo hiệu, kẻ tấn công có thể dừng các phiên, chuyển hướng kết nối và ghi lại, thay đổi hoặc chèn nội dung.
Yếu tố quan trọng nhất trong việc bảo mật tín hiệu là sử dụng các giao thức bảo mật (HTTPS và WSS (ví dụ: TLS)) để đảm bảo rằng các thông báo không thể bị chặn khi chưa mã hoá. Ngoài ra, hãy cẩn thận để không phát các thông báo báo hiệu theo cách mà những người gọi khác có thể truy cập bằng cùng một máy chủ báo hiệu.
Sau khi báo hiệu: Sử dụng ICE để đối phó với NAT và tường lửa
Đối với việc báo hiệu siêu dữ liệu, các ứng dụng WebRTC sử dụng một máy chủ trung gian, nhưng đối với việc truyền trực tuyến dữ liệu và nội dung nghe nhìn thực tế sau khi thiết lập một phiên, RTCPeerConnection
sẽ cố gắng kết nối trực tiếp hoặc ngang hàng với các máy khách.
Trong một thế giới đơn giản hơn, mọi điểm cuối WebRTC sẽ có một địa chỉ duy nhất mà điểm cuối đó có thể trao đổi với các thiết bị ngang hàng khác để giao tiếp trực tiếp.

Trên thực tế, hầu hết các thiết bị đều nằm sau một hoặc nhiều lớp NAT, một số thiết bị có phần mềm diệt virus chặn một số cổng và giao thức nhất định, đồng thời nhiều thiết bị nằm sau các proxy và tường lửa của công ty. Tường lửa và NAT có thể được triển khai bằng cùng một thiết bị, chẳng hạn như bộ định tuyến Wi-Fi tại nhà.

Các ứng dụng WebRTC có thể sử dụng khung ICE để khắc phục những phức tạp của mạng thực tế. Để điều này xảy ra, ứng dụng của bạn phải truyền URL máy chủ ICE đến RTCPeerConnection
, như mô tả trong bài viết này.
ICE cố gắng tìm ra đường dẫn tốt nhất để kết nối các thiết bị ngang hàng. Hệ thống sẽ thử tất cả các khả năng song song và chọn phương án hiệu quả nhất. Trước tiên, ICE sẽ cố gắng kết nối bằng địa chỉ máy chủ nhận được từ hệ điều hành và thẻ mạng của thiết bị. Nếu không thành công (đối với các thiết bị nằm sau NAT), ICE sẽ lấy địa chỉ bên ngoài bằng cách sử dụng máy chủ STUN và nếu không thành công, lưu lượng truy cập sẽ được định tuyến thông qua máy chủ chuyển tiếp TURN.
Nói cách khác, máy chủ STUN được dùng để lấy địa chỉ mạng bên ngoài và máy chủ TURN được dùng để chuyển tiếp lưu lượng truy cập nếu kết nối trực tiếp (ngang hàng) không thành công.
Mọi máy chủ TURN đều hỗ trợ STUN. Máy chủ TURN là một máy chủ STUN có thêm chức năng chuyển tiếp tích hợp. ICE cũng xử lý được sự phức tạp của chế độ thiết lập NAT. Trên thực tế, kỹ thuật NAT hole-punching có thể cần nhiều hơn chỉ một địa chỉ IP:cổng công khai.
URL cho máy chủ STUN và/hoặc TURN (không bắt buộc) được chỉ định bởi một ứng dụng WebRTC trong đối tượng cấu hình iceServers
. Đây là đối số đầu tiên cho hàm khởi tạo RTCPeerConnection
. Đối với appr.tc, giá trị đó sẽ có dạng như sau:
{
'iceServers': [
{
'urls': 'stun:stun.l.google.com:19302'
},
{
'urls': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'urls': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}
Sau khi RTCPeerConnection
có thông tin đó, quy trình ICE sẽ tự động diễn ra. RTCPeerConnection
sử dụng khung ICE để tìm ra đường dẫn tốt nhất giữa các thiết bị ngang hàng, hoạt động với các máy chủ STUN và TURN khi cần thiết.
STUN
NAT cung cấp cho thiết bị một địa chỉ IP để sử dụng trong mạng cục bộ riêng tư, nhưng địa chỉ này không thể sử dụng bên ngoài. Nếu không có địa chỉ công khai, các thành phần ngang hàng WebRTC sẽ không thể giao tiếp. Để giải quyết vấn đề này, WebRTC sử dụng STUN.
Các máy chủ STUN hoạt động trên Internet công cộng và có một nhiệm vụ đơn giản là kiểm tra địa chỉ IP:cổng của một yêu cầu đến (từ một ứng dụng chạy sau NAT) và gửi địa chỉ đó trở lại dưới dạng phản hồi. Nói cách khác, ứng dụng này sử dụng một máy chủ STUN để khám phá IP:cổng của ứng dụng theo góc độ công khai. Quy trình này cho phép một thành phần ngang hàng WebRTC tự lấy địa chỉ có thể truy cập công khai rồi truyền địa chỉ đó cho một thành phần ngang hàng khác thông qua cơ chế báo hiệu để thiết lập một đường liên kết trực tiếp. (Trên thực tế, các NAT hoạt động theo nhiều cách và có thể có nhiều lớp NAT, nhưng nguyên tắc vẫn như vậy.)
Máy chủ STUN không cần phải làm nhiều hoặc nhớ nhiều, vì vậy, máy chủ STUN có cấu hình tương đối thấp có thể xử lý một số lượng lớn yêu cầu.
Hầu hết các cuộc gọi WebRTC đều kết nối thành công bằng STUN – 86% theo Webrtcstats.com, mặc dù tỷ lệ này có thể thấp hơn đối với các cuộc gọi giữa những người ngang hàng đằng sau tường lửa và cấu hình NAT phức tạp.

TURN
RTCPeerConnection
cố gắng thiết lập giao tiếp trực tiếp giữa các thiết bị ngang hàng qua UDP. Nếu không thành công, RTCPeerConnection
sẽ chuyển sang TCP. Nếu không thành công, bạn có thể dùng máy chủ TURN làm phương án dự phòng để chuyển tiếp dữ liệu giữa các điểm cuối.
Xin nhắc lại rằng TURN được dùng để chuyển tiếp luồng âm thanh, video và dữ liệu giữa các thiết bị ngang hàng, chứ không phải dữ liệu báo hiệu!
Máy chủ TURN có địa chỉ công khai, vì vậy, các máy chủ này có thể được các thiết bị ngang hàng liên hệ ngay cả khi các thiết bị ngang hàng đó nằm sau tường lửa hoặc proxy. Máy chủ TURN có một nhiệm vụ đơn giản về mặt khái niệm – chuyển tiếp luồng dữ liệu. Tuy nhiên, không giống như máy chủ STUN, máy chủ TURN vốn tiêu tốn rất nhiều băng thông. Nói cách khác, máy chủ TURN cần phải mạnh mẽ hơn.

Sơ đồ này cho thấy TURN đang hoạt động. STUN thuần tuý không thành công, vì vậy mỗi thành phần ngang hàng sẽ dùng đến máy chủ TURN.
Triển khai máy chủ STUN và TURN
Để kiểm thử, Google chạy một máy chủ STUN công khai, stun.l.google.com:19302, như được dùng bởi appr.tc. Đối với dịch vụ STUN/TURN sản xuất, hãy sử dụng rfc5766-turn-server. Mã nguồn cho máy chủ STUN và TURN có trên GitHub. Tại đây, bạn cũng có thể tìm thấy các đường liên kết đến một số nguồn thông tin về việc cài đặt máy chủ. Ngoài ra, bạn cũng có thể dùng hình ảnh máy ảo cho Amazon Web Services.
Một máy chủ TURN thay thế là restund, có sẵn dưới dạng mã nguồn và cũng có sẵn cho AWS. Sau đây là hướng dẫn cách thiết lập restund trên Compute Engine.
- Mở tường lửa nếu cần cho tcp=443, udp/tcp=3478.
- Tạo 4 phiên bản, mỗi phiên bản cho một IP công khai, hình ảnh Ubuntu 12.06 tiêu chuẩn.
- Thiết lập cấu hình tường lửa cục bộ (cho phép BẤT KỲ từ BẤT KỲ).
- Công cụ cài đặt:
shell sudo apt-get install make sudo apt-get install gcc
- Cài đặt libre từ creytiv.com/re.html.
- Tìm nạp restund từ creytiv.com/restund.html và giải nén./.
wget
hancke.name/restund-auth.patch và áp dụng bằngpatch -p1 < restund-auth.patch
.- Chạy
make
,sudo make install
cho libre và restund. - Điều chỉnh
restund.conf
cho phù hợp với nhu cầu của bạn (thay thế địa chỉ IP và đảm bảo địa chỉ này chứa cùng một khoá bí mật dùng chung) rồi sao chép vào/etc
. - Sao chép
restund/etc/restund
vào/etc/init.d/
. - Định cấu hình restund:
- Đặt
LD_LIBRARY_PATH
. - Sao chép
restund.conf
vào/etc/restund.conf
. - Đặt
restund.conf
để sử dụng 10 bên phải. Địa chỉ IP.
- Đặt
- Chạy restund
- Kiểm thử bằng ứng dụng stund từ máy từ xa:
./client IP:port
Ngoài cuộc gọi một đối một: WebRTC nhiều bên
Bạn cũng có thể xem xét tiêu chuẩn IETF mà Justin Uberti đề xuất cho API REST để truy cập vào các Dịch vụ TURN.
Bạn có thể dễ dàng hình dung các trường hợp sử dụng tính năng phát trực tuyến nội dung nghe nhìn không chỉ là một cuộc gọi đơn giản giữa hai người. Ví dụ: hội nghị truyền hình giữa một nhóm đồng nghiệp hoặc một sự kiện công khai có một diễn giả và hàng trăm hoặc hàng triệu người xem.
Một ứng dụng WebRTC có thể sử dụng nhiều RTCPeerConnection để mọi điểm cuối kết nối với mọi điểm cuối khác trong cấu hình dạng lưới. Đây là phương pháp được các ứng dụng như talky.io áp dụng và hoạt động hiệu quả đối với một số ít người dùng ngang hàng. Ngoài ra, mức tiêu thụ băng thông và mức xử lý trở nên quá mức, đặc biệt là đối với các ứng dụng di động.

Ngoài ra, một ứng dụng WebRTC có thể chọn một điểm cuối để phân phối luồng đến tất cả các điểm cuối khác trong cấu hình hình sao. Bạn cũng có thể chạy một điểm cuối WebRTC trên máy chủ và tạo cơ chế phân phối lại của riêng mình (webrtc.org cung cấp một ứng dụng khách mẫu).
Kể từ Chrome 31 và Opera 18, bạn có thể dùng một MediaStream
từ một RTCPeerConnection
làm đầu vào cho một MediaStream
khác. Điều này có thể cho phép các cấu trúc linh hoạt hơn vì nó cho phép một ứng dụng web xử lý việc định tuyến cuộc gọi bằng cách chọn kết nối với người ngang hàng khác. Để xem ví dụ này trong thực tế, hãy xem WebRTC samples Peer connection relay (WebRTC mẫu: Chuyển tiếp kết nối ngang hàng) và WebRTC samples Multiple peer connections (WebRTC mẫu: Nhiều kết nối ngang hàng).
Thiết bị điều khiển đa điểm
Một lựa chọn tốt hơn cho nhiều điểm cuối là sử dụng Thiết bị điều khiển đa điểm (MCU). Đây là một máy chủ hoạt động như một cầu nối để phân phối nội dung nghe nhìn giữa một số lượng lớn người tham gia. MCU có thể xử lý nhiều độ phân giải, codec và tốc độ khung hình trong một hội nghị truyền hình; xử lý việc chuyển mã; thực hiện chuyển tiếp luồng có chọn lọc; đồng thời trộn hoặc ghi âm thanh và video. Đối với cuộc gọi nhiều bên, bạn cần cân nhắc một số vấn đề, đặc biệt là cách hiển thị nhiều nguồn video và trộn âm thanh từ nhiều nguồn. Các nền tảng đám mây, chẳng hạn như vLine, cũng cố gắng tối ưu hoá việc định tuyến lưu lượng truy cập.
Bạn có thể mua một gói phần cứng MCU hoàn chỉnh hoặc tự tạo gói phần cứng của riêng mình.

Có một số lựa chọn phần mềm MCU nguồn mở. Ví dụ: Licode (trước đây là Lynckia) tạo ra một MCU nguồn mở cho WebRTC. OpenTok có Mantis.
Không chỉ là trình duyệt: VoIP, điện thoại và tin nhắn
Bản chất tiêu chuẩn hoá của WebRTC giúp bạn có thể thiết lập giao tiếp giữa một ứng dụng WebRTC đang chạy trong trình duyệt và một thiết bị hoặc nền tảng đang chạy trên một nền tảng giao tiếp khác, chẳng hạn như điện thoại hoặc hệ thống hội nghị truyền hình.
SIP là một giao thức báo hiệu được dùng bởi các hệ thống VoIP và hội nghị truyền hình. Để cho phép giao tiếp giữa một ứng dụng web WebRTC và một ứng dụng SIP (chẳng hạn như hệ thống hội nghị truyền hình), WebRTC cần một máy chủ proxy để điều phối tín hiệu. Tín hiệu phải đi qua cổng nhưng sau khi thiết lập được thông tin liên lạc, lưu lượng truy cập SRTP (video và âm thanh) có thể truyền trực tiếp ngang hàng.
Mạng điện thoại chuyển mạch công cộng (PSTN) là mạng chuyển mạch vòng của tất cả các điện thoại tương tự "thông thường". Đối với các cuộc gọi giữa ứng dụng web WebRTC và điện thoại, lưu lượng truy cập phải đi qua một cổng PSTN. Tương tự, các ứng dụng web WebRTC cần một máy chủ XMPP trung gian để giao tiếp với các điểm cuối Jingle, chẳng hạn như ứng dụng nhắn tin tức thời. Jingle được Google phát triển như một phần mở rộng của XMPP để hỗ trợ thoại và video cho các dịch vụ nhắn tin. Các hoạt động triển khai WebRTC hiện tại dựa trên thư viện C++ libjingle, một hoạt động triển khai Jingle ban đầu được phát triển cho Talk.
Một số ứng dụng, thư viện và nền tảng tận dụng khả năng giao tiếp với thế giới bên ngoài của WebRTC:
- sipML5: một ứng dụng SIP JavaScript nguồn mở
- jsSIP: Thư viện SIP JavaScript
- Phono: API điện thoại JavaScript mã nguồn mở được tạo dưới dạng một trình bổ trợ
- Zingaya: một tiện ích điện thoại có thể nhúng
- Twilio: cuộc gọi thoại và nhắn tin
- Uberconference: hội nghị truyền hình
Các nhà phát triển sipML5 cũng đã tạo ra cổng webrtc2sip. Tethr và Tropo đã minh hoạ một khung cho hoạt động liên lạc trong trường hợp thảm hoạ "trong một chiếc cặp" bằng cách sử dụng một ô OpenBTS để cho phép liên lạc giữa điện thoại phổ thông và máy tính thông qua WebRTC. Đó là cách liên lạc qua điện thoại mà không cần nhà mạng!
Tìm hiểu thêm
Lớp học lập trình WebRTC cung cấp hướng dẫn từng bước về cách tạo ứng dụng trò chuyện video và văn bản bằng dịch vụ báo hiệu Socket.io chạy trên Node.
Bài thuyết trình về WebRTC tại Google I/O năm 2013 của Justin Uberti, trưởng nhóm công nghệ WebRTC
Bài thuyết trình của Chris Wilson tại SFHTML5 – Giới thiệu về các ứng dụng WebRTC
Cuốn sách dài 350 trang WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web (WebRTC: API và giao thức RTCWEB của web theo thời gian thực HTML5) cung cấp nhiều thông tin chi tiết về đường dẫn dữ liệu và tín hiệu, đồng thời có một số sơ đồ chi tiết về cấu trúc liên kết mạng.
WebRTC và báo hiệu: Những điều chúng tôi học được trong 2 năm – Bài đăng trên blog của TokBox về lý do việc loại bỏ báo hiệu khỏi thông số kỹ thuật là một ý tưởng hay
Hướng dẫn thực tế về cách tạo ứng dụng WebRTC của Ben Strong cung cấp nhiều thông tin về các cấu trúc liên kết và cơ sở hạ tầng WebRTC.
Chương về WebRTC trong cuốn sách Mạng trình duyệt hiệu suất cao của Ilya Grigorik đi sâu vào cấu trúc, các trường hợp sử dụng và hiệu suất của WebRTC.