شروع با اتصالات همتا

اتصالات نظیر بخشی از مشخصات WebRTC است که به اتصال دو برنامه در رایانه‌های مختلف برای برقراری ارتباط با استفاده از پروتکل نظیر به نظیر می‌پردازد. ارتباط بین نظیرها می‌تواند ویدیویی، صوتی یا داده‌های دودویی دلخواه (برای کلاینت‌هایی که از API RTCDataChannel پشتیبانی می‌کنند) باشد. برای کشف نحوه اتصال دو نظیر، هر دو کلاینت باید پیکربندی سرور ICE را ارائه دهند. این یا یک STUN یا یک سرور TURN است و نقش آنها ارائه کاندیداهای ICE به هر کلاینت است که سپس به نظیر راه دور منتقل می‌شوند. این انتقال کاندیداهای ICE معمولاً سیگنالینگ نامیده می‌شود.

سیگنالینگ

مشخصات WebRTC شامل APIهایی برای ارتباط با یک سرور ICE (Internet Connectivity Establishment) است، اما مؤلفه سیگنالینگ بخشی از آن نیست. سیگنالینگ برای اینکه دو همتا نحوه اتصال خود را به اشتراک بگذارند، مورد نیاز است. معمولاً این مشکل از طریق یک 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() به عنوان شرح محلی تنظیم می‌شود و سپس از طریق کانال سیگنالینگ ما به سمت گیرنده ارسال می‌شود. ما همچنین یک شنونده برای کانال سیگنالینگ خود تنظیم می‌کنیم تا زمانی که پاسخی به شرح جلسه ارائه شده ما از سمت گیرنده دریافت می‌شود، آن را بشنود.

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

وقتی دو همتا، شرح جلسات محلی و راه دور را تنظیم کردند، از قابلیت‌های همتای راه دور مطلع می‌شوند. این به معنای آماده بودن ارتباط بین همتاها نیست. برای اینکه این کار انجام شود، باید نامزدهای ICE را در هر همتا جمع‌آوری کرده و (از طریق کانال سیگنالینگ) به همتای دیگر منتقل کنیم.

نامزدهای ICE

قبل از اینکه دو همتا بتوانند با استفاده از WebRTC ارتباط برقرار کنند، باید اطلاعات اتصال را تبادل کنند. از آنجایی که شرایط شبکه می‌تواند بسته به عوامل مختلفی متفاوت باشد، معمولاً از یک سرویس خارجی برای کشف نامزدهای احتمالی اتصال به یک همتا استفاده می‌شود. این سرویس ICE نام دارد و از یک سرور STUN یا TURN استفاده می‌کند. STUN مخفف Session Traversal Utilities for NAT است و معمولاً به طور غیرمستقیم در اکثر برنامه‌های WebRTC استفاده می‌شود.

TURN (پیمایش با استفاده از رله NAT) راهکار پیشرفته‌تری است که پروتکل‌های STUN را در خود جای داده و اکثر سرویس‌های تجاری مبتنی بر WebRTC از یک سرور TURN برای برقراری ارتباط بین همتاها استفاده می‌کنند. API WebRTC مستقیماً از STUN و TURN پشتیبانی می‌کند و تحت عنوان کامل‌تر Internet Connectivity Establishment گردآوری شده است. هنگام ایجاد یک اتصال WebRTC، معمولاً یک یا چند سرور ICE را در پیکربندی شیء RTCPeerConnection ارائه می‌دهیم.

یخ قطره‌ای

پس از ایجاد یک شیء RTCPeerConnection ، چارچوب زیربنایی از سرورهای ICE ارائه شده برای جمع‌آوری کاندیداها برای برقراری اتصال (کاندیداهای ICE) استفاده می‌کند. رویداد icegatheringstatechange در RTCPeerConnection نشان می‌دهد که جمع‌آوری ICE در چه وضعیتی است ( new ، gathering یا complete ).

اگرچه یک همتا می‌تواند تا زمان تکمیل جمع‌آوری ICE منتظر بماند، اما معمولاً استفاده از تکنیک "یخ قطره‌ای" و انتقال هر کاندید ICE به همتای راه دور به محض کشف آن، بسیار کارآمدتر است. این امر به طور قابل توجهی زمان راه‌اندازی اتصال همتا را کاهش می‌دهد و امکان شروع تماس ویدیویی با تأخیر کمتر را فراهم می‌کند.

برای جمع‌آوری کاندیدهای ICE، کافیست یک شنونده برای رویداد icecandidate اضافه کنید. RTCPeerConnectionIceEvent که در آن شنونده منتشر می‌شود، حاوی ویژگی 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، باید انتظار داشته باشیم که وضعیت اتصال همتا در نهایت به وضعیت متصل تغییر کند. برای تشخیص این موضوع، یک شنونده به RTCPeerConnection خود اضافه می‌کنیم که در آن به رویدادهای connectionstatechange گوش می‌دهیم.

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

مستندات API RTCPeerConnection