Bắt đầu kết nối với ứng dụng ngang hàng

Kết nối ngang hàng là một phần của quy cách WebRTC, giúp kết nối hai ứng dụng trên các máy tính khác nhau để giao tiếp bằng giao thức ngang hàng. Thông tin giao tiếp giữa các máy ngang hàng có thể là video, âm thanh hoặc dữ liệu nhị phân tuỳ ý (đối với các ứng dụng hỗ trợ API RTCDataChannel). Để khám phá cách hai máy ngang hàng có thể kết nối, cả hai ứng dụng khách đều cần cung cấp cấu hình Máy chủ ICE. Đây là một máy chủ STUN hoặc TURN và vai trò của chúng là cung cấp các đề xuất ICE cho từng ứng dụng, sau đó được chuyển đến máy chủ đồng cấp từ xa. Quá trình chuyển đổi các ứng cử viên ICE này thường được gọi là tín hiệu.

Báo hiệu

Quy cách WebRTC bao gồm các API để giao tiếp với Máy chủ ICE (Thiết lập kết nối Internet), nhưng thành phần báo hiệu không thuộc quy cách này. Cần có tín hiệu để hai máy ngang hàng chia sẻ cách kết nối. Thông thường, vấn đề này được giải quyết thông qua một API Web dựa trên HTTP thông thường (tức là dịch vụ REST hoặc cơ chế RPC khác) trong đó các ứng dụng web có thể chuyển tiếp thông tin cần thiết trước khi bắt đầu kết nối ngang hàng.

Đoạn mã sau đây cho biết cách sử dụng dịch vụ báo hiệu giả định này để gửi và nhận thông báo không đồng bộ. Phương thức này sẽ được sử dụng trong các ví dụ còn lại trong hướng dẫn này nếu cần.

// Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
    // New message from remote client received
});

// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');

Có nhiều cách để triển khai tín hiệu và thông số kỹ thuật WebRTC không ưu tiên bất kỳ giải pháp cụ thể nào.

Bắt đầu kết nối ngang hàng

Mỗi kết nối ngang hàng được xử lý bằng một đối tượng RTCPeerConnection. Hàm khởi tạo cho lớp này lấy một đối tượng RTCConfiguration làm tham số. Đối tượng này xác định cách thiết lập kết nối ngang hàng và phải chứa thông tin về các máy chủ ICE cần sử dụng.

Sau khi tạo RTCPeerConnection, chúng ta cần tạo một lời đề nghị hoặc câu trả lời SDP, tuỳ thuộc vào việc chúng ta là bên gọi hay bên nhận. Sau khi tạo đề nghị hoặc câu trả lời SDP, bạn phải gửi đề nghị hoặc câu trả lời đó đến máy tính từ xa thông qua một kênh khác. Việc truyền các đối tượng SDP đến các máy ngang hàng từ xa được gọi là báo hiệu và không thuộc phạm vi của quy cách WebRTC.

Để bắt đầu thiết lập kết nối ngang hàng từ phía gọi, chúng ta tạo một đối tượng RTCPeerConnection, sau đó gọi createOffer() để tạo một đối tượng RTCSessionDescription. Nội dung mô tả phiên này được đặt làm nội dung mô tả cục bộ bằng setLocalDescription(), sau đó được gửi qua kênh báo hiệu của chúng tôi đến bên nhận. Chúng tôi cũng thiết lập trình nghe cho kênh báo hiệu của mình khi nhận được câu trả lời cho nội dung mô tả phiên mà chúng tôi cung cấp từ bên nhận.

async function makeCall() {
    const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
    const peerConnection = new RTCPeerConnection(configuration);
    signalingChannel.addEventListener('message', async message => {
        if (message.answer) {
            const remoteDesc = new RTCSessionDescription(message.answer);
            await peerConnection.setRemoteDescription(remoteDesc);
        }
    });
    const offer = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offer);
    signalingChannel.send({'offer': offer});
}

Ở phía nhận, chúng ta đợi một ưu đãi sắp tới trước khi tạo thực thể RTCPeerConnection. Sau khi hoàn tất, chúng ta sẽ đặt ưu đãi đã nhận bằng setRemoteDescription(). Tiếp theo, chúng ta gọi createAnswer() để tạo câu trả lời cho ưu đãi đã nhận được. Câu trả lời này được đặt làm nội dung mô tả cục bộ bằng cách sử dụng setLocalDescription(), sau đó được gửi đến bên gọi qua máy chủ báo hiệu của chúng tôi.

const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
    if (message.offer) {
        peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        signalingChannel.send({'answer': answer});
    }
});

Sau khi hai máy ngang hàng thiết lập cả nội dung mô tả phiên cục bộ và từ xa, chúng sẽ biết được chức năng của máy ngang hàng từ xa. Điều này không có nghĩa là kết nối giữa các máy ngang hàng đã sẵn sàng. Để phương thức này hoạt động, chúng ta cần thu thập các đề xuất ICE tại mỗi máy ngang hàng và chuyển (qua kênh báo hiệu) đến máy ngang hàng khác.

Ứng viên ICE

Trước khi hai máy ngang hàng có thể giao tiếp bằng WebRTC, chúng cần trao đổi thông tin về khả năng kết nối. Vì điều kiện mạng có thể thay đổi tuỳ thuộc vào một số yếu tố, nên dịch vụ bên ngoài thường được dùng để khám phá các ứng cử viên có thể kết nối với một máy ngang hàng. Dịch vụ này được gọi là ICE và đang sử dụng máy chủ STUN hoặc TURN. STUN là viết tắt của Session Traversal Utilities for NAT (Tiện ích chuyển tiếp phiên cho NAT) và thường được sử dụng gián tiếp trong hầu hết các ứng dụng WebRTC.

TURN (Chuyển tiếp bằng cách sử dụng NAT chuyển tiếp) là giải pháp nâng cao hơn, kết hợp các giao thức STUN và hầu hết các dịch vụ thương mại dựa trên WebRTC đều sử dụng máy chủ TURN để thiết lập kết nối giữa các máy ngang hàng. API WebRTC hỗ trợ trực tiếp cả STUN và TURN, đồng thời được thu thập theo thuật ngữ đầy đủ hơn là Thiết lập kết nối Internet. Khi tạo một kết nối WebRTC, chúng ta thường cung cấp một hoặc nhiều máy chủ ICE trong cấu hình cho đối tượng RTCPeerConnection.

Trickle ICE

Sau khi tạo đối tượng RTCPeerConnection, khung cơ bản sẽ sử dụng các máy chủ ICE được cung cấp để thu thập các đề xuất thiết lập kết nối (đề xuất ICE). Sự kiện icegatheringstatechange trên RTCPeerConnection cho biết trạng thái thu thập thông tin ICE (new, gathering hoặc complete).

Mặc dù một máy khách có thể đợi cho đến khi quá trình thu thập ICE hoàn tất, nhưng việc sử dụng kỹ thuật "trickle ice" (lưu lượng băng thông nhỏ) và truyền từng đề xuất ICE đến máy khách từ xa khi phát hiện được thường hiệu quả hơn nhiều. Điều này sẽ giúp giảm đáng kể thời gian thiết lập kết nối ngang hàng và cho phép bắt đầu cuộc gọi video với độ trễ ít hơn.

Để thu thập các đề xuất ICE, bạn chỉ cần thêm trình nghe cho sự kiện icecandidate. RTCPeerConnectionIceEvent được phát trên trình nghe đó sẽ chứa thuộc tính candidate đại diện cho một đề xuất mới cần được gửi đến máy ngang hàng từ xa (Xem phần Tín hiệu).

// Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
    if (event.candidate) {
        signalingChannel.send({'new-ice-candidate': event.candidate});
    }
});

// Listen for remote ICE candidates and add them to the local RTCPeerConnection
signalingChannel.addEventListener('message', async message => {
    if (message.iceCandidate) {
        try {
            await peerConnection.addIceCandidate(message.iceCandidate);
        } catch (e) {
            console.error('Error adding received ice candidate', e);
        }
    }
});

Đã thiết lập kết nối

Sau khi nhận được các đề xuất ICE, chúng ta có thể dự kiến trạng thái của kết nối ngang hàng sẽ thay đổi thành trạng thái đã kết nối. Để phát hiện điều này, chúng ta thêm trình nghe vào RTCPeerConnection để nghe các sự kiện connectionstatechange.

// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
    if (peerConnection.connectionState === 'connected') {
        // Peers connected!
    }
});

Tài liệu về API RTCPeerConnection