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

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

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

טכנולוגיות שמאפשרות לשרת לשלוח נתונים ללקוח ברגע שהוא יודע שיש נתונים חדשים קיימות כבר זמן מה. הם נקראים בשמות כמו Push או Comet. אחת מהדרכים הנפוצות ביותר ליצור אשליה של חיבור שהשרת יזם נקראת "תשאול ארוך" (long polling). בשיטת דגימה ארוכה, הלקוח פותח חיבור HTTP לשרת, והחיבור נשאר פתוח עד שהשרת שולח תשובה. בכל פעם שיש לשרת נתונים חדשים, הוא שולח את התגובה (טכניקות אחרות כוללות בקשות Flash, XHR multipart ומה שנקרא htmlfiles). תשאול ארוך ושיטות אחרות פועלים די טוב. אתם משתמשים בהם כל יום באפליקציות כמו הצ'אט של Gmail.

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

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

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

תחילת העבודה

כדי לפתוח חיבור WebSocket, פשוט קוראים ל-WebSocket constructor:

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

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

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

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

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

// 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. כדי לציין את הפורמט של הקובץ הבינארי שהתקבל, מגדירים את המאפיין binaryType של אובייקט WebSocket לערך blob או arraybuffer. פורמט ברירת המחדל הוא blob. (לא צריך להתאים את הפרמטר binaryType בשליחה).

// 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, שאפשר לשלב בקלות בכל סביבת אינטרנט על ידי מתן HTTP API לשליחת הודעות WebSocket ללקוחות. בגלל בקשת ה-HTTP הנוספת, תמיד יהיה תקורה נוספת בהשוואה ל-WebSocket טהור.

צד השרת

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

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

גרסאות של פרוטוקולים

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

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

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

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

הדגמות

קובצי עזר