This entry is part 5 of 5 in the series #1 WebRTC Code Labs

Trong bài lab này, các bạn sẽ được thực hành một API nữa là WebRTC RTCDataChannel để gửi các loại dữ liệu tùy ý, cụ thể trong bài lab này dữ liệu là một đoạn văn bản được gửi trên cùng một trang web. Như vậy chúng ta sẽ phải cài đặt một kết nối giữa 2 peer với nhau sử dụng RTCPeerConnection ở bài thực hành trước, sau đó sẽ thực hiện gửi văn bản giữa 2 bên cho nhau. Hãy xem RTCDataChannel hoạt động như thế nào ?

Nội dung của bài lab:

  1. Tạo một trang web HTML5 mới. (Mới hoàn toàn 😀 )
  2. Thêm 2 thẻ textarea và các nút tương ứng
  3. Viết mã JavaScript gọi WebRTC API thứ 3: RTCDataChannel
  4. Xem trang web từ localhost
  5. Gợi ý tự khám phá

Bước 1: Tạo tài liệu mới HTML5

Hãy tạo một trang HTML5 đặt tên là wcl-session04.html và có nội dung sau: Thêm 2 thẻ textarea và các nút tương ứng.

<!DOCTYPE html>
<html>

<head>

<meta name="keywords" content="JavaScript, WebRTC" />
<meta name="description" content="WebRTC codelab" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1">

<title>WebRTC codelab: Session 04</title>

<style>
</style>

</head>

<body>

  <textarea id="dataChannelSend" disabled placeholder="Press Start, enter some text, then press Send."></textarea>
  <textarea id="dataChannelReceive" disabled></textarea>

  <div id="buttons">
    <button id="startButton">Start</button>
    <button id="sendButton">Send</button>
    <button id="closeButton">Stop</button>
  </div>

<style>
// your code here !!

</style>

</body>

</html>

Bước 2: Viết mã JavaScript trên trang html

Trong bước này, bạn sẽ thêm vào trang html ở trên một đoạn mã javascript vào trong thẻ script ở bước trên, khai báo các biến handle các nút điều khiển. Đồng thời gán sự kiện onclick của các nút vào các hàm xử lý tương ứng createConnection, sendData, closeDataChannels sẽ được định nghĩa trong bước sau:

    var sendChannel, receiveChannel;

    var startButton = document.getElementById("startButton");
    var sendButton = document.getElementById("sendButton");
    var closeButton = document.getElementById("closeButton");
    startButton.disabled = false;
    sendButton.disabled = true;
    closeButton.disabled = true;
    startButton.onclick = createConnection;
    sendButton.onclick = sendData;
    closeButton.onclick = closeDataChannels;

Bước 3: Gửi dữ liệu lên kênh truyền

Hãy sửa đoạn mã javascript trong bước 2, bằng cách thêm vào các hàm xử lý createConnection, sendData, closeDataChannels.

1. createConnection

    function trace(text) {
        console.log((performance.now() / 1000).toFixed(3) + ": " + text);
    }

    function createConnection() {
        var servers = null;
        window.localPeerConnection = new webkitRTCPeerConnection(servers,
        {optional: [{RtpDataChannels: true}]});
        trace('Created local peer connection object localPeerConnection');

        try {
            // Reliable Data Channels not yet supported in Chrome
            sendChannel = localPeerConnection.createDataChannel("sendDataChannel",
              {reliable: false});
            trace('Created send data channel');
        } catch (e) {
            alert('Failed to create data channel. ' +
                  'You need Chrome M25 or later with RtpDataChannel enabled');
            trace('createDataChannel() failed with exception: ' + e.message);
        }
        localPeerConnection.onicecandidate = gotLocalCandidate;
        sendChannel.onopen = handleSendChannelStateChange;
        sendChannel.onclose = handleSendChannelStateChange;

        window.remotePeerConnection = new webkitRTCPeerConnection(servers,
        {optional: [{RtpDataChannels: true}]});
        trace('Created remote peer connection object remotePeerConnection');

        remotePeerConnection.onicecandidate = gotRemoteIceCandidate;
        remotePeerConnection.ondatachannel = gotReceiveChannel;

        localPeerConnection.createOffer(gotLocalDescription);
        startButton.disabled = true;
        closeButton.disabled = false;
    }

2. sendData

Cú pháp của API RTCDataChannel rất giống với các hàm send() và sự kiện message trong Websocket. Hãy chú ý các ràng buộc được sử dụng.

function sendData() {
    var data = document.getElementById("dataChannelSend").value;
    sendChannel.send(data);
    trace('Sent data: ' + data);
}

3. closeDataChannels

function closeDataChannels() {
    trace('Closing data channels');
    sendChannel.close();
    trace('Closed data channel with label: ' + sendChannel.label);
    receiveChannel.close();
    trace('Closed data channel with label: ' + receiveChannel.label);
    localPeerConnection.close();
    remotePeerConnection.close();
    localPeerConnection = null;
    remotePeerConnection = null;
    trace('Closed peer connections');
    startButton.disabled = false;
    sendButton.disabled = true;
    closeButton.disabled = true;
    dataChannelSend.value = "";
    dataChannelReceive.value = "";
    dataChannelSend.disabled = true;
    dataChannelSend.placeholder = "Press Start, enter some text, then press Send.";
}

Bước 3: Xử lý Peer Connection

Tương tự như API trước, chúng ta cũng cần thêm vào các hàm xử các thông tin của người ở đầu bên kia. Vì lý do này, hãy thêm vào đoạn mã javascript là các hàm nhập và xử lý thông tin dữ liệu metadata được mô tả trong bản tin SDP, và các thông tin ICE Candidates. Bạn sẽ nhận ra những hàm này đã được sử dụng trong bài lab trước.

function gotLocalDescription(desc) {
    localPeerConnection.setLocalDescription(desc);
    trace('Offer from localPeerConnection \n' + desc.sdp);
    remotePeerConnection.setRemoteDescription(desc);
    remotePeerConnection.createAnswer(gotRemoteDescription);
}

function gotRemoteDescription(desc) {
    remotePeerConnection.setLocalDescription(desc);
    trace('Answer from remotePeerConnection \n' + desc.sdp);
    localPeerConnection.setRemoteDescription(desc);
}

function gotLocalCandidate(event) {
    trace('local ice callback');
    if (event.candidate) {
        remotePeerConnection.addIceCandidate(event.candidate);
        trace('Local ICE candidate: \n' + event.candidate.candidate);
    }
}

function gotRemoteIceCandidate(event) {
    trace('remote ice callback');
    if (event.candidate) {
        localPeerConnection.addIceCandidate(event.candidate);
        trace('Remote ICE candidate: \n ' + event.candidate.candidate);
    }
}

Bước 4: Nhận dữ liệu từ kênh truyền

Bước quan trọng nhất là bạn nhận và xử lý dữ liệu như thế nào khi làm việc với RTCDataChannel. Đoạn mã sau đây sẽ được xử lý khi kênh truyền dữ liệu được thiết lập giữa 2 peer với nhau. Một hàm sẽ thực hiện nhận dữ liệu, hiển thị vào tron textarea có id là dataChannelReceive.

function gotReceiveChannel(event) {
    trace('Receive Channel Callback');
    receiveChannel = event.channel;
    receiveChannel.onmessage = handleMessage;
    receiveChannel.onopen = handleReceiveChannelStateChange;
    receiveChannel.onclose = handleReceiveChannelStateChange;
}

function handleMessage(event) {
    trace('Received message: ' + event.data);
    document.getElementById("dataChannelReceive").value = event.data;
}

function handleSendChannelStateChange() {
    var readyState = sendChannel.readyState;
    trace('Send channel state is: ' + readyState);
    if (readyState == "open") {
        dataChannelSend.disabled = false;
        dataChannelSend.focus();
        dataChannelSend.placeholder = "";
        sendButton.disabled = false;
        closeButton.disabled = false;
    } else {
        dataChannelSend.disabled = true;
        sendButton.disabled = true;
        closeButton.disabled = true;
    }
}

function handleReceiveChannelStateChange() {
    var readyState = receiveChannel.readyState;
    trace('Receive channel state is: ' + readyState);
}

Bước 5: Chạy thử trên localhost

Hãy lưu lại file chương trình và khởi động here --port 8000

About The Author

  • Phong Ngô Hồng

    Sao làm giống như thế thì k đc ad ơi.
    Kiểm tra trên phpDesigner thì nó báo lỗi:
    1. Trong bước 2 có thẻ

    // your code here !!

    có đúng là thẻ Style hay là thẻ Script.
    2. Hai dòng:

    thì nó báo lỗi là cái disabled cần phải là disabled=””

    Những sữa xong lại k chạy đc chổ bấm start r nhưng không ghi đc text và gửi cũng k đc

  • Mía mụn

    Vấn đề là state ko được thiết lập, “Send channel state is: closed”, không biết lỗi này là do đâu nhỉ?! :~