שליחת נתונים בין דפדפנים עם ערוצי נתונים WebRTC

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

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

למה עוד ערוץ נתונים?

יש לנו WebSocket, ‏ AJAX ו-Server Sent Events. למה אנחנו צריכים עוד ערוץ תקשורת? WebSocket הוא דו-כיווני, אבל כל הטכנולוגיות האלה מיועדות לתקשורת אל שרת או משרת.

RTCDataChannel משתמשת בגישה שונה:

  • הוא פועל עם ממשק ה-API של RTCPeerConnection, שמאפשר קישוריות מקצה לקצה. כך אפשר לצמצם את זמן האחזור – אין שרת ביניים ופחות 'קפיצות'.
  • RTCDataChannel משתמש ב-Stream Control Transmission Protocol‏ (SCTP), שמאפשר הגדרה של סמנטיקה של העברה מחוץ לסדר והגדרה של שליחה חוזרת.

RTCDataChannel זמין עכשיו עם תמיכה ב-SCTP במחשב וב-Android בדפדפנים Google Chrome,‏ Opera ו-Firefox.

אזהרה: איתות, STUN ו-TURN

WebRTC מאפשר תקשורת בין שווים, אבל עדיין נדרשים שרתים לאיתור כדי להחליף מטא-נתונים של מדיה ורשתות כדי להפעיל חיבור בין שווים.

WebRTC מתמודד עם NAT וחומות אש באמצעות:

  • מסגרת ICE כדי לקבוע את נתיב הרשת הטוב ביותר האפשרי בין שווים.
  • שרתי STUN כדי לקבוע כתובת IP ויציאה שגלויות לכולם לכל עמית.
  • שרתי TURN אם החיבור הישיר נכשל ונדרש העברת נתונים.

למידע נוסף על האופן שבו WebRTC פועל עם שרתים לשליחת אותות ולניהול רשתות, קראו את המאמר WebRTC בעולם האמיתי: STUN,‏ TURN ו-signaling.

היכולות

ה-API של RTCDataChannel תומך בקבוצה גמישה של סוגי נתונים. ה-API תוכנן לחקות את WebSocket בדיוק, ו-RTCDataChannel תומך במחרוזות וגם בחלק מהסוגים הבינאריים ב-JavaScript, כמו Blob,‏ ArrayBuffer ו-ArrayBufferView. הסוגים האלה יכולים להיות מועילים כשעובדים עם העברת קבצים ומשחקים מרובי משתתפים.

RTCDataChannel יכול לפעול במצב לא מהימן ולא מסודר (בדומה ל-User Datagram Protocol או UDP), במצב מהימן ומסודר (בדומה ל-Transmission Control Protocol או TCP) ובמצבים מהימנים חלקית:

  • המצב 'מהימן ומסודר' מבטיח את העברת ההודעות ואת הסדר שבו הן נמסרות. הפעולה הזו דורשת תקורה נוספת, ולכן יכול להיות שהמצב הזה יהיה איטי יותר.
  • במצב לא מהימן ולא מסודר אין ערובה שכל ההודעות יגיעו לצד השני, או בסדר שבו הן נשלחו. כך אפשר להסיר את התקורה, וכך המצב הזה פועל מהר יותר.
  • המצב 'אמין חלקית' מבטיח את העברת ההודעה בתנאים ספציפיים, כמו זמן קצוב לשליחת ההודעה מחדש או מספר מקסימלי של שליחות חוזרות. אפשר גם להגדיר את סדר ההודעות.

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

לפניכם טבלה מועילה מתוך High Performance Browser Networking מאת Ilya Grigorik:

TCPUDPSCTP
אמינותאמינותהמידע לא אמיןניתן להגדרה
משלוחהזמנה שלללא סדרניתן להגדרה
הפצהמבוססת-בייטהודעות מונחותהודעות
בקרת זרימהכןלאכן
בקרת עומסכןלאכן

בשלב הבא תלמדו איך להגדיר את RTCDataChannel לשימוש במצב מהימן ומסודר או במצב לא מהימן ולא מסודר.

הגדרת ערוצי נתונים

יש כמה הדגמות פשוטות של RTCDataChannel באינטרנט:

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

הקוד הנדרש כדי להתחיל הוא קצר:

const peerConnection = new RTCPeerConnection();

// Establish your peer connection using your signaling channel here
const dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = (error) => {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = (event) => {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = () => {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = () => {
  console.log("The Data Channel is Closed");
};

האובייקט dataChannel נוצר מחיבור שכבר הוקם בין שווים. אפשר ליצור אותו לפני או אחרי האות. לאחר מכן מעבירים תווית כדי להבדיל את הערוץ הזה מערוצים אחרים וקבוצה של הגדרות אישיות אופציונליות:

const dataChannelOptions = {
  ordered: false, // do not guarantee order
  maxPacketLifeTime: 3000, // in milliseconds
};

אפשר גם להוסיף את האפשרות maxRetransmits (מספר הפעמים לניסיון לפני הכשל), אבל אפשר לציין רק את maxRetransmits או את maxPacketLifeTime, ולא את שניהם. לסמנטיקה של UDP, מגדירים את maxRetransmits כ-0 ואת ordered כ-false. מידע נוסף זמין במסמכי ה-RFC הבאים של IETF: Stream Control Transmission Protocol ו-Stream Control Transmission Protocol Partial Reliability Extension.

  • ordered: אם רצוי שהנתונים יעברו בערוץ מסודר או לא
  • maxPacketLifeTime: הזמן המקסימלי לניסיון לשדר מחדש הודעה שנכשלה
  • maxRetransmits: מספר הפעמים המקסימלי לניסיון לשדר מחדש הודעה שנכשלה
  • protocol: מאפשר להשתמש בפרוטוקול משנה, שמספק מטא-נתונים לאפליקציה
  • negotiated: אם ההגדרה היא true, המערכת לא תגדיר באופן אוטומטי ערוץ נתונים בצד השני, ותצטרכו ליצור ערוץ נתונים עם אותו מזהה בצד השני בעצמכם.
  • id: מאפשר לכם לספק מזהה משלכם לערוץ, שאפשר להשתמש בו רק בשילוב עם negotiated שמוגדר כ-true)

רוב האנשים צריכים להשתמש רק בשלוש האפשרויות הראשונות: ordered,‏ maxPacketLifeTime ו-maxRetransmits. ב-SCTP (שכל הדפדפנים שתומכים ב-WebRTC משתמשים בו עכשיו) הערכים 'מהימן' ו'מסודר' מוגדרים כ-true כברירת מחדל. אם רוצים שליטה מלאה משכבת האפליקציה, כדאי להשתמש בנתונים לא מהימנים ולא מסודרים, אבל ברוב המקרים כדאי להשתמש בנתונים מהימנים חלקית.

שימו לב שכמו ב-WebSocket, RTCDataChannel יוצר אירועים כשמתבצע חיבור, כשהחיבור נסגר או כשיש שגיאות, וכשהוא מקבל הודעה מהצינור השני.

זה בטוח?

הצפנה היא חובה בכל הרכיבים של WebRTC. ב-RTCDataChannel, כל הנתונים מאובטחים באמצעות Transport Layer Security ל-Datagram‏ (DTLS). DTLS הוא נגזרת של SSL, כלומר הנתונים שלכם יהיו מאובטחים כמו בכל חיבור רגיל שמבוסס על SSL. DTLS הוא תקן שמובנה בכל הדפדפנים שתומכים ב-WebRTC. מידע נוסף זמין ב-Wireshark wiki.

שינוי האופן שבו אתם חושבים על נתונים

טיפול בכמויות גדולות של נתונים יכול להיות נקודת חולשה ב-JavaScript. כפי שציינו המפתחים של Sharefest, כדי לעשות זאת נדרשה חשיבה חדשה על נתונים. אם אתם מעבירים קובץ גדול יותר מנפח הזיכרון הפנוי, תצטרכו לחשוב על דרכים חדשות לשמירת המידע הזה. כאן נכנסות לתמונה טכנולוגיות כמו FileSystem API, כפי שמתואר בהמשך.

פיתוח אפליקציה לשיתוף קבצים

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

כדי לבצע העברה מוצלחת, צריך לבצע כמה שלבים:

  1. קריאת קובץ ב-JavaScript באמצעות File API.
  2. ליצור חיבור בין לקוחות באמצעות RTCPeerConnection.
  3. יצירת ערוץ נתונים בין לקוחות באמצעות RTCDataChannel.

יש כמה נקודות שכדאי לזכור כשמנסים לשלוח קבצים דרך RTCDataChannel:

  • גודל הקובץ: אם גודל הקובץ קטן יחסית וניתן לאחסן אותו ולטעון אותו כ-Blob אחד, אפשר לטעון אותו לזיכרון באמצעות File API ואז לשלוח את הקובץ כפי שהוא דרך ערוץ מהימן (עם זאת, חשוב לזכור שבדפדפנים יש מגבלות על גודל ההעברה המקסימלי). ככל שגודל הקובץ גדול יותר, הדברים נעשים מורכבים יותר. כשנדרש מנגנון חלוקה למקטעים, מקטעי הקבצים נטענים ונשלחים לצומת אחר, יחד עם המטא-נתונים של chunkID כדי שהצומת יוכל לזהות אותם. חשוב לזכור שבמקרה כזה, צריך גם לשמור את הקטעים קודם באחסון אופליין (לדוגמה, באמצעות FileSystem API) ולשמור אותם בדיסק של המשתמש רק כשהקובץ נמצא במלואו.
  • גודל מקטע: אלה 'האטומים' הקטנים ביותר של הנתונים באפליקציה. חלוקה למקטעים נדרשת כי יש כרגע מגבלת גודל לשליחה (הבעיה הזו תיפתר בגרסה עתידית של ערוצי הנתונים). ההמלצה הנוכחית לגבי גודל מקטע מקסימלי היא 64KB.

אחרי שהקובץ יועבר במלואו לצד השני, אפשר יהיה להוריד אותו באמצעות תג עוגן:

function saveFile(blob) {
  const link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

אפליקציות שיתוף הקבצים האלה ב-PubShare וב-GitHub משתמשות בשיטה הזו. שתיהן הן קוד פתוח, והן מספקות בסיס טוב לאפליקציית שיתוף קבצים שמבוססת על RTCDataChannel.

אז מה אפשר לעשות?

RTCDataChannel פותח דרכים חדשות לפתח אפליקציות לשיתוף קבצים, למשחקים מרובי משתתפים ולמסירת תוכן.

  • שיתוף קבצים בין משתמשים (P2P) כפי שמתואר למעלה
  • משחקים מרובי משתתפים בשילוב עם טכנולוגיות אחרות, כמו WebGL, כפי שמוצג ב-BananaBread של Mozilla
  • העברת תוכן כפי שהיא מחדשת על ידי PeerCDN, מסגרת שמעבירה נכסי אינטרנט באמצעות תקשורת נתונים מקצה לקצה

שינוי אופן הפיתוח של אפליקציות

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

הופעתו של RTCDataChannel יכולה לשנות את האופן שבו אתם חושבים על העברת נתונים בדפדפן.

למידע נוסף