תחילת העבודה עם חיבורים של אפליקציות להשוואה

חיבורים בין אפליקציות הם חלק מהמפרטים של WebRTC הקשורים לחיבור של שתי אפליקציות במחשבים שונים כדי לתקשר באמצעות פרוטוקול מקצה לקצה. התקשורת בין אפליקציות דומות יכולה להיות וידאו, אודיו או נתונים בינאריים שרירותיים (ללקוחות שתומכים ב-RTCDataChannel API). כדי לגלות איך שני עמיתים יכולים להתחבר, שני הלקוחות צריכים לספק הגדרת ICE שרת. זהו שרת STUN או שרת טרנינג, ותפקידו הוא לספק מועמדי ICE לכל לקוח, שיועבר לאחר מכן לעמית המרוחק. ההעברה הזו של מועמדי ICE נקראת בדרך כלל אותות.

איתות

המפרט של WebRTC כולל ממשקי API לתקשורת עם שרת ICE (אינטרנט קישוריות), אבל רכיב האותות אינו חלק ממנו. יש צורך בסימון כדי ששני עמיתים יוכלו לשתף איתם מידע. בדרך כלל הבעיה נפתרת דרך ממשק 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(), ולאחר מכן הוא נשלח דרך ערוץ האותות שלנו לצד המקבל. הגדרנו גם פונקציות event 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});
    }
});

לאחר ששתי האפליקציות האחרות הגדירו את התיאור המקומי וגם את התיאורים של הביקורים מרחוק, הם מכירים את היכולות של אפליקציות דומות. זה לא אומר שהחיבור בין האפליקציות להשוואה מוכן. כדי לבצע את הפעולה הזו, אנחנו צריכים לאסוף את המועמדים ICE בכל אחד מהעמיתים ולהעביר אותם (באמצעות ערוץ האותות) לעמית השני.

מועמדים בניסויי ICE

לפני שחברים אחרים יכולים לתקשר באמצעות WebRTC, הם צריכים להחליף פרטי קישוריות. מכיוון שתנאי הרשת עשויים להשתנות בהתאם למספר גורמים, שירות חיצוני משמש בדרך כלל לגילוי המועמדים האפשריים להתחברות. השירות הזה נקרא ICE ומשתמש בשרת STUN או בשרת מסתובב. STUN הוא קיצור דרך סשן כלי עזר ל-NAT, ובדרך כלל משתמשים בו ברוב האפליקציות של WebRTC.

turn (מעבר דרך שרת ממסר) הוא הפתרון המתקדם יותר שמשלב את הפרוטוקולים של STUN, ורוב השירותים מבוססי ה-WebRTC משתמשים בשרת פניות כדי ליצור חיבורים בין עמיתים. ה-API של WebRTC תומך הן ב-STUN וגם בסיבוב ישיר, והוא נאסף במסגרת המונח המלא יותר של קישוריות אינטרנט. כשיוצרים חיבור WebRTC, בדרך כלל אנחנו מעניקים שרת ICE אחד או יותר בתצורה של האובייקט RTCPeerConnection.

טריק ICE

לאחר יצירת אובייקט RTCPeerConnection, המסגרת הבסיסית משתמשת בשרתי ICE שסופקו כדי לאסוף מועמדים להקמת קישוריות (ICE מועמדים). האירוע icegatheringstatechange בתאריך RTCPeerConnection מציין באיזה מצב נמצא איסוף ה-ICE (new, gathering או complete).

על אף שעמית יכול להמתין עד שהאיסוף ב-ICE מסתיים, הוא בדרך כלל יעיל הרבה יותר להשתמש בטכניקת &צר משולשת; להעביר מועמד ל-ICE לעמית מרוחק כאשר הוא מתגלה. פעולה זו תפחית באופן משמעותי את זמן ההגדרה של הקישוריות להשוואה ותאפשר לשיחת וידאו להתחיל לעבוד עם פחות עיכובים.

כדי לאסוף מועמדי ICE, צריך להוסיף פונקציות event listener לאירוע 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!
    }
});

תיעוד של RTCPeerConnection API