נעים להכיר: WebSockets – הוספת שקעים לאינטרנט

מאלטה אובל
מאלטה אובל

הבעיה: חיבורי שרת-לקוח ושרת לקוח עם זמן אחזור קצר

האינטרנט נבנה בעיקר סביב הפרדיגמה 'בקשה/תגובה' של HTTP. לקוח טוען דף אינטרנט ולא קורה דבר עד שהמשתמש לוחץ כדי לעבור לדף הבא. בסביבות 2005, AJAX התחילה להפוך את האינטרנט לדינמי יותר. עדיין, כל תקשורת HTTP נוהלה על ידי הלקוח, מה שדרש אינטראקציה של המשתמש או סקרים תקופתיים כדי לטעון נתונים חדשים מהשרת.

טכנולוגיות שמאפשרות לשרת לשלוח נתונים ללקוח ברגע שבו ידוע שיש נתונים חדשים זמינים, כבר לא מעט זמן. הם מופיעים בשמות כמו 'דחיפה' או 'שמיכה'. אחת מפעולות הפריצה הנפוצות ביותר ליצירת אשליה של חיבור המופעל על ידי שרת נקראת 'סקרים ארוכים'. באמצעות סקרים ארוכים, הלקוח פותח חיבור HTTP לשרת ששומר אותו פתוח עד לשליחת התגובה. בכל פעם שיש לשרת נתונים חדשים, הוא שולח את התגובה (שיטות אחרות כוללות Flash, בקשות XHR מרובי חלקים, שנקראות קובצי htmlfiles). סקרים ארוכים והטכניקות האחרות עובדות היטב. אתה משתמש בהם מדי יום ביישומים כגון צ'אט של GMail.

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

הכירו את WebSocket: הצגת sockets לאינטרנט

המפרט של WebSocket מגדיר API שיוצר חיבורי 'socket' בין דפדפן אינטרנט לשרת. במילים פשוטות: יש חיבור מתמשך בין הלקוח לשרת, ושני הצדדים יכולים להתחיל לשלוח נתונים בכל שלב.

תחילת העבודה

פותחים חיבור WebSocket פשוט על ידי קריאה לבונה ה-WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

שימו לב ל-ws:. זוהי הסכימה החדשה של כתובת ה-URL עבור חיבורי WebSocket. יש גם wss: לחיבור מאובטח ל-WebSocket באותו אופן שבו משתמשים ב-https: לחיבורי HTTP מאובטחים.

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

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

השמות של פרוטוקולי המשנה חייבים להיות אחד מהשמות של פרוטוקולי המשנה הרשומים במרשם IANA. נכון לפברואר 2012 יש רק שם אחד של פרוטוקול משנה (סבון).

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

תקשורת עם השרת

ברגע שיהיה לנו חיבור לשרת (כשהאירוע open מופעל) נוכל להתחיל לשלוח נתונים לשרת באמצעות השיטה send('your message') באובייקט החיבור. בעבר הוא תמך רק במחרוזות, אבל במפרט החדש הוא יכול עכשיו לשלוח גם הודעות בינאריות. כדי לשלוח נתונים בינאריים, אפשר להשתמש באובייקט Blob או ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

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

WebSocket יכול גם לקבל הודעות בינאריות במפרט העדכני ביותר. פריימים בינאריים יתקבלו בפורמט Blob או ArrayBuffer. כדי לציין את הפורמט של הקובץ הבינארי שהתקבל, יש להגדיר את המאפייןטיפל בינארי של אובייקט WebSocket ל-blob או ל-arraybuffer. פורמט ברירת המחדל הוא blob. (לא צריך ליישר את הפרמטר relatedType בשליחה).

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

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

// Determining accepted extensions
console.log(connection.extensions);

תקשורת בין מקורות

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

שרתי Proxy

כל טכנולוגיה חדשה מגיעה עם קבוצה חדשה של בעיות. במקרה של WebSocket, זו התאימות עם שרתי proxy שמשמשים לתווך חיבורי HTTP ברוב הרשתות של החברה. פרוטוקול WebSocket משתמש במערכת השדרוג של HTTP (שבדרך כלל משמשת ל-HTTP/SSL) כדי 'לשדרג' חיבור HTTP לחיבור WebSocket. חלק משרתי ה-proxy לא אוהבים את זה ויתנתקו בחיבור. לכן, גם אם לקוח נתון משתמש בפרוטוקול WebSocket, ייתכן שלא תהיה אפשרות ליצור חיבור. לכן הקטע הבא חשוב עוד יותר :)

השתמשו ב-WebSockets היום

WebSocket הוא עדיין טכנולוגיה צעירה שלא מיושמת באופן מלא בכל הדפדפנים. עם זאת, כבר היום אפשר להשתמש ב-WebSocket עם ספריות שמשתמשות באחת מהחלופות שצוינו למעלה, בכל פעם ש-WebSocket לא זמין. ספרייה שהפכה לפופולרית מאוד בדומיין הזה היא socket.io שמגיע עם לקוח והטמעה של שרת של הפרוטוקול, כולל חלופות (החל מפברואר 2012, socket.io לא תומך בהעברת הודעות בינאריות). קיימים גם פתרונות מסחריים כמו PusherApp, שאותם ניתן לשלב בקלות בכל סביבת אינטרנט על ידי אספקת ממשק API של HTTP לצורך שליחת הודעות WebSocket ללקוחות. בגלל בקשת ה-HTTP הנוספת, תמיד תהיה תקורה נוספת בהשוואה ל-WebSocket טהור.

בצד השרת

השימוש ב-WebSocket יוצר דפוס שימוש חדש לגמרי לאפליקציות בצד השרת. על אף שמקבצי שרת מסורתיים, כמו LAMP, תוכננו סביב מחזור הבקשה/התגובה של HTTP, לרוב הם לא מתנהגים היטב עם מספר גדול של חיבורי WebSocket פתוחים. כדי להשאיר מספר גדול של חיבורים פתוחים בו-זמנית, נדרשת ארכיטקטורה שמקבלת בו-זמניות גבוהה בעלות ביצועים נמוכה. ארכיטקטורות כאלה מתוכננות בדרך כלל סביב שרשור (thread) (כלומר, IO ללא חסימה).

הטמעות בצד השרת

גרסאות פרוטוקול

הפרוטוקול הקווי (לחיצת יד והעברת הנתונים בין הלקוח לשרת) ב-WebSocket הוא עכשיו RFC6455. הגרסאות העדכניות של Chrome ו-Chrome ל-Android תואמות באופן מלא ל-RFC6455, כולל העברת הודעות בינאריות. בנוסף, Firefox יהיה תואם לגרסה 11, ל-Internet Explorer בגרסה 10. עדיין אפשר להשתמש בגרסאות פרוטוקול ישנות יותר, אבל זה לא מומלץ כי הן מוכרות כפגיעות. אם יש לך יישומי שרת עבור גרסאות ישנות יותר של פרוטוקול WebSocket, מומלץ לשדרג אותו לגרסה העדכנית ביותר.

תרחישים לדוגמה

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

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

הדגמות

קובצי עזר