เริ่มต้นใช้งานการเชื่อมต่อเพียร์

การเชื่อมต่อแบบเพียร์เป็นส่วนหนึ่งของข้อกำหนด WebRTC ที่จัดการกับการเชื่อมต่อแอปพลิเคชัน 2 แอปพลิเคชันในคอมพิวเตอร์เครื่องอื่นเพื่อสื่อสารโดยใช้โปรโตคอลแบบเพียร์ต่อเพียร์ การสื่อสารระหว่างเพียร์อาจเป็นวิดีโอ เสียง หรือข้อมูลไบนารีแบบกำหนดเอง (สำหรับไคลเอ็นต์ที่รองรับ RTCDataChannel API) หากต้องการดูว่าคู่สนทนา 2 คนจะเชื่อมต่อได้อย่างไร ไคลเอ็นต์ทั้ง 2 ฝ่ายต้องระบุการกําหนดค่า ICE Server ซึ่งอาจเป็นเซิร์ฟเวอร์ STUN หรือ TURN โดยบทบาทของเซิร์ฟเวอร์เหล่านี้คือการระบุตัวเลือก ICE ให้กับไคลเอ็นต์แต่ละราย จากนั้นจึงโอนไปยังคู่สนทนาระยะไกล การส่งผู้สมัคร ICE นี้มักเรียกว่าการส่งสัญญาณ

การส่งสัญญาณ

ข้อกำหนดของ WebRTC มี API สำหรับการสื่อสารกับเซิร์ฟเวอร์ ICE (Internet Connectivity Establishment) แต่ไม่มีคอมโพเนนต์การส่งสัญญาณ จำเป็นต้องใช้การส่งสัญญาณเพื่อให้ผู้ใช้ 2 รายแชร์วิธีเชื่อมต่อ โดยปกติแล้วปัญหานี้แก้ไขได้ผ่าน Web API ที่ใช้ HTTP ปกติ (เช่น REST Service หรือกลไก RPC อื่นๆ) ซึ่งเว็บแอปพลิเคชันสามารถส่งต่อข้อมูลที่จำเป็นได้ก่อนที่จะเริ่มการเชื่อมต่อแบบ Peer

ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้บริการส่งสัญญาณสมมตินี้เพื่อส่งและรับข้อความแบบไม่พร้อมกัน ซึ่งจะใช้ในตัวอย่างที่เหลือในคู่มือนี้เมื่อจําเป็น

// 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!');

การส่งสัญญาณสามารถใช้งานได้หลายวิธี และข้อกำหนดของ WebRTC ไม่ได้กำหนดให้ใช้โซลูชันใดโซลูชันหนึ่งโดยเฉพาะ

เริ่มต้นการเชื่อมต่อเพียร์

การเชื่อมต่อแบบเพียร์แต่ละรายการจะจัดการโดยออบเจ็กต์ RTCPeerConnection ตัวสร้างสำหรับคลาสนี้ใช้ออบเจ็กต์ RTCConfiguration รายการเดียวเป็นพารามิเตอร์ ออบเจ็กต์นี้จะกำหนดวิธีตั้งค่าการเชื่อมต่อแบบ Peer และควรมีข้อมูลเกี่ยวกับเซิร์ฟเวอร์ ICE ที่จะใช้

เมื่อสร้าง RTCPeerConnection แล้ว เราจะต้องสร้างข้อเสนอหรือคำตอบ SDP โดยขึ้นอยู่กับว่าเราเป็นคู่สนทนาที่โทรออกหรือรับสาย เมื่อสร้างข้อเสนอหรือคำตอบ SDP แล้ว จะต้องส่งไปยังคู่สนทนาระยะไกลผ่านช่องทางอื่น การส่งออบเจ็กต์ SDP ไปยังคู่สนทนาระยะไกลเรียกว่าการส่งสัญญาณ ซึ่งข้อกำหนดของ WebRTC ไม่ได้ครอบคลุมถึง

หากต้องการเริ่มการตั้งค่าการเชื่อมต่อแบบ Peer จากฝั่งที่โทรออก เราจะสร้างออบเจ็กต์ RTCPeerConnection แล้วเรียกใช้ createOffer() เพื่อสร้างออบเจ็กต์ RTCSessionDescription ระบบจะตั้งค่าคําอธิบายเซสชันนี้เป็นคําอธิบายในเครื่องโดยใช้ setLocalDescription() จากนั้นส่งผ่านช่องทางการรับส่งสัญญาณไปยังฝั่งที่รับ นอกจากนี้ เรายังตั้งค่าโปรแกรมฟังช่องสัญญาณเพื่อรับคําตอบสําหรับคําอธิบายเซสชันที่เราเสนอจากฝั่งที่รับด้วย

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});
}

ฝั่งที่รับ เราจะรอข้อเสนอขาเข้าก่อนที่จะสร้างRTCPeerConnectionอินสแตนซ์ เมื่อดำเนินการเสร็จแล้ว เราจะตั้งค่าข้อเสนอที่ได้รับโดยใช้ setRemoteDescription() จากนั้นเราจะเรียกใช้ createAnswer() เพื่อสร้างคำตอบสำหรับข้อเสนอที่ได้รับ ระบบจะตั้งค่าคำตอบนี้เป็นคำอธิบายในเครื่องโดยใช้ setLocalDescription() จากนั้นส่งไปยังฝั่งที่โทรผ่านเซิร์ฟเวอร์การรับส่งสัญญาณ

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});
    }
});

เมื่อคู่สนทนาทั้ง 2 รายตั้งค่าทั้งคําอธิบายเซสชันในเครื่องและระยะไกลแล้ว ก็จะทราบความสามารถของคู่สนทนาระยะไกล แต่ไม่ได้หมายความว่าการเชื่อมต่อระหว่างคู่สนทนาพร้อมใช้งานแล้ว เราต้องรวบรวมตัวเลือก ICE ของพาร์ทเนอร์แต่ละรายและโอน (ผ่านช่องทางการรับส่งสัญญาณ) ไปยังพาร์ทเนอร์รายอื่นเพื่อให้วิธีนี้ใช้งานได้

ผู้สมัคร ICE

ก่อนที่จะสื่อสารโดยใช้ WebRTC ได้ พาร์ทเนอร์ 2 รายต้องแลกเปลี่ยนข้อมูลการเชื่อมต่อ เนื่องจากสภาพเครือข่ายอาจแตกต่างกันไปตามปัจจัยหลายประการ จึงมักใช้บริการภายนอกเพื่อค้นหาตัวเลือกที่เป็นไปได้สำหรับการเชื่อมต่อกับเพียร์ บริการนี้เรียกว่า ICE และใช้เซิร์ฟเวอร์ STUN หรือ TURN STUN ย่อมาจาก Session Traversal Utility for NAT และมักใช้ในแอปพลิเคชัน WebRTC ส่วนใหญ่โดยอ้อม

TURN (Traversal Using Relay NAT) เป็นโซลูชันขั้นสูงขึ้นที่รวมโปรโตคอล STUN และบริการ WebRTC เชิงพาณิชย์ส่วนใหญ่ใช้เซิร์ฟเวอร์ TURN เพื่อสร้างการเชื่อมต่อระหว่างคู่สนทนา WebRTC API รองรับทั้ง STUN และ TURN โดยตรง และรวบรวมข้อมูลภายใต้คําที่สมบูรณ์ยิ่งขึ้นอย่างการสร้างการเชื่อมต่ออินเทอร์เน็ต เมื่อสร้างการเชื่อมต่อ WebRTC เรามักจะระบุเซิร์ฟเวอร์ ICE อย่างน้อย 1 เซิร์ฟเวอร์ในการกําหนดค่าสําหรับออบเจ็กต์ RTCPeerConnection

Trickle ICE

เมื่อสร้างออบเจ็กต์ RTCPeerConnection แล้ว เฟรมเวิร์กพื้นฐานจะใช้เซิร์ฟเวอร์ ICE ที่ระบุเพื่อรวบรวมผู้สมัครสำหรับการสร้างการเชื่อมต่อ (ICEcandidates) เหตุการณ์ icegatheringstatechange ในวันที่ RTCPeerConnection บ่งบอกสถานะการรวบรวมข้อมูล ICE (new, gathering หรือ complete)

แม้ว่าพาร์ทเนอร์จะรอจนกว่าการเก็บรวบรวม ICE จะเสร็จสมบูรณ์ได้ แต่โดยทั่วไปแล้วการใช้เทคนิค "ICE แบบทีละน้อย" และส่งตัวเลือก ICE แต่ละรายการไปยังพาร์ทเนอร์ระยะไกลเมื่อค้นพบตัวเลือกนั้นๆ จะมีประสิทธิภาพมากกว่า ซึ่งจะช่วยลดเวลาในการตั้งค่าการเชื่อมต่อแบบ Peer ได้มากและช่วยให้เริ่มวิดีโอคอลได้เร็วขึ้น

หากต้องการรวบรวมผู้สมัคร ICE ให้เพิ่ม Listener สําหรับเหตุการณ์ icecandidate RTCPeerConnectionIceEvent ที่ส่งใน Listener นั้นจะมีพร็อพเพอร์ตี้ candidate ที่แสดงถึงตัวเลือกใหม่ซึ่งควรส่งไปยังคู่สนทนาระยะไกล (ดู "การส่งสัญญาณ")

// 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);
        }
    }
});

เชื่อมต่อแล้ว

เมื่อได้รับตัวเลือกการเชื่อมต่อ ICE แล้ว เราคาดว่าสถานะการเชื่อมต่อแบบ Peer จะเปลี่ยนเป็น "เชื่อมต่อแล้ว" ในท้ายที่สุด หากต้องการตรวจหาเหตุการณ์นี้ เราจะเพิ่ม Listener ลงใน RTCPeerConnection ซึ่งจะคอยฟังเหตุการณ์ connectionstatechange

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

เอกสารประกอบของ RTCPeerConnection API