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

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

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

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

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

// 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 รายการเดียวเป็นพารามิเตอร์ ออบเจ็กต์นี้จะกําหนดวิธีตั้งค่าการเชื่อมต่อเพียร์และควรมีข้อมูลเกี่ยวกับเซิร์ฟเวอร์ ICE ที่จะใช้

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

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

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

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

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

น้ําแข็ง Trickle

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

แม้ว่าผู้เรียนรู้จะรอจนกว่าการรวบรวม ICE เสร็จสิ้นได้ แต่การใช้เทคนิค &quot แบบ Trickle&quot อย่างมีประสิทธิภาพนั้นเป็นเรื่องที่มีประสิทธิภาพมากกว่า และสามารถส่งผู้สมัคร ICE แต่ละคนไปยังแอปเทียบเท่าระยะไกลได้ทันทีที่ค้นพบ วิธีนี้จะช่วยลดเวลาในการตั้งค่าการเชื่อมต่อระหว่างแอปได้อย่างมากและช่วยให้วิดีโอคอลเริ่มต้นได้อย่างรวดเร็ว

หากต้องการรวบรวมผู้สมัคร 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 แล้ว เราคาดว่าสถานะการเชื่อมต่อแอปเทียบเท่าของเราจะเปลี่ยนเป็นสถานะเชื่อมต่อในที่สุด หากตรวจพบกรณีนี้ เราจะเพิ่ม Listener ไปยัง RTCPeerConnection สําหรับฟังเหตุการณ์ connectionstatechange

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

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