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

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

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

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

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

חדש: WebSocket – הוספת רכיבי Socket לאינטרנט

במפרט 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, רשום רק שם אחד של פרוטוקול משנה (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 אחרי האירוע open. נכון לפברואר 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 פתוחים. כדי לשמור על מספר גדול של חיבורים פתוחים בו-זמנית, נדרשת ארכיטקטורה שמקבלת בו-זמנית מספר רב של בקשות בעלות נמוכה על הביצועים. בדרך כלל, ארכיטקטורות כאלה מתבססות על תהליכים או על מה שנקרא IO ללא חסימה.

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

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

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

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

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

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

הדגמות

קובצי עזר