הבעיה: חיבורים בין שרת ללקוח וחיבור בין לקוח לשרת עם זמן אחזור קצר
האינטרנט נבנה במידה רבה סביב המודל שנקרא 'בקשה/תגובה' של 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 פתוחים. כדי לשמור על מספר גדול של חיבורים פתוחים בו-זמנית, נדרשת ארכיטקטורה שמקבלת בו-זמנית מספר רב של בקשות בעלות נמוכה על הביצועים. ארכיטקטורות כאלה מתוכננות בדרך כלל סביב שרשור (threads) או כאלה שנקראים IO ללא חסימה.
הטמעות בצד השרת
- Node.js
- Java
- אבן אודם
- Python
- Erlang
- C++
- .NET
גרסאות פרוטוקול
מעכשיו, פרוטוקול הכבלים (לחיצת יד והעברת הנתונים בין הלקוח לשרת) עבור WebSocket הוא RFC6455. הגרסאות האחרונות של Chrome ו-Chrome ל-Android תואמות במלואן ל-RFC6455, כולל שליחת הודעות בינאריות. בנוסף, דפדפן Firefox יהיה תואם לגרסה 11 ודפדפן Internet Explorer יהיה תואם לגרסה 10. עדיין אפשר להשתמש בגרסאות פרוטוקול ישנות יותר, אבל לא מומלץ לעשות זאת כי ידוע שהן נחשפות להתקפות. אם יש לכם הטמעות שרת לגרסאות ישנות יותר של פרוטוקול WebSocket, מומלץ לשדרג אותן לגרסה האחרונה.
תרחישים לדוגמה
מומלץ להשתמש ב-WebSocket בכל פעם שצריך חיבור עם זמן אחזור נמוך מאוד, כמעט בזמן אמת, בין הלקוח לשרת. חשוב לזכור שיכול להיות שתצטרכו לשנות את אופן היצירה של האפליקציות בצד השרת, ולהתמקד בטכנולוגיות כמו תורים של אירועים. תרחישים לדוגמה:
- משחקים מקוונים מרובי משתתפים
- אפליקציות צ'אט
- המניה של ספורט בשידור חי
- עדכון בזמן אמת של רשתות חברתיות
הדגמות
- Plink
- Paint With Me
- Pixelatr
- קו מקווקו
- משחקי רשת מרובי משתתפים של תשבצים אונליין
- שרת Ping (שנעשה בו שימוש בדוגמאות שלמעלה)
- דוגמה ל-HTML5demos