כיצד עובד פרוטוקול WebSockets: מדריך מלא לחיבורים בזמן אמת
בימיו הראשונים של האינטרנט, הדפדפן היה מציג מסמכים פשוט. ביקשת דף, השרת רינדר אותו והחיבור נסגר. מחזור בקשה-תגובה זה הוא הליבה של HTTP (Hypertext Transfer Protocol).
עם זאת, ככל שאפליקציות אינטרנט התפתחו לחוויות אינטראקטיביות עשירות — כמו צ’אט בזמן אמת, עדכונים פיננסיים חיים, עריכה שיתופית ומשחקי רשת מרובי משתתפים — המודל המסורתי של HTTP החל להראות את מגבלותיו.
כדי לקבל עדכונים חיים, מפתחים הסתמכו בתחילה על פתרונות עוקפים:
- Short Polling (שאילתות קצרות): הדפדפן שולח שוב ושוב בקשות HTTP לשרת מדי כמה שניות כדי לבדוק אם יש מידע חדש. הדבר יוצר תקורה עצומה של כותרות (headers) ומבזבז משאבי שרת.
- Long Polling (שאילתות ארוכות / Comet): הדפדפן שולח בקשה, והשרת שומר עליה פתוחה עד שמידע חדש זמין. ברגע שהמידע נשלח, החיבור נסגר, והדפדפן פותח מיד בקשה חדשה. פתרון זה מורכב לניהול ועדיין גורר תקורה משמעותית בהקמת החיבורים.
WebSockets פתר את המגבלות הללו על ידי הצגת פרוטוקול סטנדרטי לתקשורת דו-כיוונית מתמשכת (full-duplex) על גבי חיבור TCP יחיד.
מהו WebSocket?
פרוטוקול WebSockets (המוגדר ב-RFC 6455) פועל לצד HTTP. בעוד ש-HTTP הוא פרוטוקול חסר מדינה (stateless) שבו רק הלקוח יכול ליזום בקשות, חיבור WebSocket נשאר פתוח ללא הגבלת זמן, ומאפשר הן ללקוח והן לשרת לשלוח נתונים זה לזה בכל עת עם השהיה (latency) מינימלית.
זהו כלל היסוד של WebSockets:
ברגע שהחיבור נוצר, כל אחד מהצדדים יכול לשלוח הודעות בכל עת מבלי ליזום בקשת חיבור חדשה.
מדריך שלב אחר שלב: מחזור החיים של החיבור
חיבור WebSocket עובר שלושה שלבים מובחנים: לחיצת היד (Handshake), העברת הנתונים וסגירת החיבור.
1. לחיצת היד של HTTP (שדרוג פרוטוקול)
מכיוון שחומות אש ונתבים מוגדרים לאפשר תעבורת אינטרנט סטנדרטית בפורטים 80 (HTTP) ו-443 (HTTPS), חיבורי WebSockets מתחילים את דרכם כבקשת HTTP/1.1 רגילה. זה נקרא Upgrade Handshake.
בקשת הלקוח
הלקוח שולח בקשת HTTP GET עם כותרות ספציפיות המבקשות להחליף פרוטוקול:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://example.com
Upgrade: websocketו-Connection: Upgrade: מודיעים לשרת שהלקוח מעוניין להחליף פרוטוקול.Sec-WebSocket-Key: ערך אקראי של 16 בתים המקודד ב-Base64. הוא משמש להוכחה שהשרת קיבל את בקשת לחיצת היד ומבין את פרוטוקול ה-WebSocket.Sec-WebSocket-Version: מציין את גרסת פרוטוקול ה-WebSocket (בדרך כלל 13).Origin: משמש את השרת כדי להחליט אם לאפשר את החיבור (בדיקת אבטחה נגד חיבורים מאתרים לא מורשים).
תגובת השרת
אם השרת תומך ב-WebSockets, הוא מאמת את הבקשה ומגיב עם קוד סטטוס HTTP 101 Switching Protocols:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- כיצד השרת מחשב את
Sec-WebSocket-Accept:- השרת לוקח את ערך ה-
Sec-WebSocket-Keyשל הלקוח (dGhlIHNhbXBsZSBub25jZQ==). - הוא משרשר אותו עם מחרוזת GUID קבועה:
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11". - הוא מחשב את ה-hash מסוג SHA-1 של המחרוזת המשולבת.
- הוא מקודד את ה-hash שנוצר ב-Base64.
- אם הלקוח מוודא שהערך הזה מתאים לציפיותיו, לחיצת היד מצליחה, חיבור ה-HTTP הופך לחיבור TCP ישיר, ושני הצדדים עוברים להשתמש בפרוטוקול WebSocket.
- השרת לוקח את ערך ה-
2. מסגור נתונים והעברה
בניגוד ל-HTTP, ששולח כותרות טקסט פשוט ולאחריהן את גוף ההודעה, WebSockets מעביר נתונים בחבילות בינאריות מובנות הנקראות פריימים (frames).
לפריים של WebSocket יש כותרת קלה מאוד (בין 2 ל-14 בתים) ולאחריה ה-payload. כותרת זו מכילה:
- ביט FIN (ביט 1): מציין אם זהו הפריים האחרון של ההודעה.
- Opcode (4 ביטים): מגדיר את סוג הפריים:
0x1: פריים טקסט (מקודד ב-UTF-8)0x2: פריים בינארי0x8: בקשה לסגירת החיבור0x9: Ping0xA: Pong
- ביט מסכה (Mask bit) (ביט 1): מציין אם נתוני ה-payload ממוסכים.
- אורך ה-Payload: גודל הנתונים.
- מפתח מסכה (4 בתים): דרישת אבטחה קריטית: כל הפריימים שנשלחים מהלקוח לשרת חייבים להיות ממוסכים (על ידי פעולת XOR) באמצעות מפתח אקראי של 4 בתים. זה מונע משרתי פרוקסי לקרוא את התעבורה או לבצע התקפות הרעלת מטמון. פריימים מהשרת ללקוח אינם צריכים להיות ממוסכים.
פעימות לב (Ping/Pong)
כדי למנוע מנתבים ומאזני עומסים לסגור חיבורים שאינם פעילים, כל צד יכול לשלוח פריים Ping. הצד המקבל חייב להגיב מיד עם פריים Pong המכיל את אותו ה-payload.
3. סגירת החיבור
כדי לסגור חיבור בצורה נקייה:
- קצה אחד שולח פריים
Closeהמכיל קוד סטטוס (למשל,1000לסגירה רגילה,1006לסגירה חריגה) וסיבה טקסטואלית אופציונלית. - הקצה השני מגיב עם פריים
Closeמשלו. - חיבור ה-TCP שמתחת נסגר.
דוגמת קוד: מימוש WebSocket ב-Node.js
כדי לראות את WebSockets בפעולה, נכתוב אפליקציית Node.js פשוטה. ניצור שרת WebSocket מקומי שמחזיר (echo) כל הודעה שהוא מקבל, לצד סקריפט לקוח שמתחבר אליו.
שרת ה-WebSocket (server.js)
const { WebSocketServer } = require('ws');
const http = require('http');
// 1. יצירת שרת HTTP סטנדרטי
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('HTTP Server running. Use WebSocket to connect.\n');
});
// 2. חיבור שרת WebSocket לשרת ה-HTTP
const wss = new WebSocketServer({ server });
wss.on('connection', (ws, req) => {
const clientIp = req.socket.remoteAddress;
console.log(`[שרת] לקוח חדש התחבר מכתובת ${clientIp}`);
// שליחת הודעת ברוכים הבאים ללקוח
ws.send(JSON.stringify({ type: 'welcome', message: 'מחובר לשרת ה-WebSocket של Ghaznix!' }));
// האזנה להודעות נכנסות מלקוח זה
ws.on('message', (message) => {
console.log(`[שרת] התקבל: ${message}`);
try {
const data = JSON.parse(message);
ws.send(JSON.stringify({
type: 'echo',
message: `הד מהשרת: ${data.text.toUpperCase()}`,
timestamp: new Date().toISOString()
}));
} catch (e) {
ws.send(JSON.stringify({ type: 'error', message: 'פורמט JSON לא תקין' }));
}
});
// טיפול בניתוק הלקוח
ws.on('close', (code, reason) => {
console.log(`[שרת] לקוח התנתק (קוד: ${code}, סיבה: ${reason.toString() || 'אין'})`);
});
ws.on('error', (error) => {
console.error(`[שרת] שגיאת סוקט: ${error.message}`);
});
});
server.listen(8080, () => {
console.log('שרת ה-WebSocket מאזין בכתובת ws://localhost:8080');
});
לקוח הדפדפן (JavaScript בצד הלקוח)
ניתן להריץ לקוח זה ישירות בקונסולה של הדפדפן שלכם:
// 1. יצירת חיבור לשרת
const socket = new WebSocket('ws://localhost:8080');
// 2. טיפול בפתיחת החיבור
socket.addEventListener('open', (event) => {
console.log('[לקוח] מחובר לשרת.');
const payload = JSON.stringify({ text: 'שלום, שרת!' });
socket.send(payload);
console.log(`[לקוח] נשלח: ${payload}`);
});
// 3. האזנה להודעות מהשרת
socket.addEventListener('message', (event) => {
const response = JSON.parse(event.data);
console.log('[לקוח] התקבלה הודעה מהשרת:', response);
});
// 4. האזנה לסגירת החיבור
socket.addEventListener('close', (event) => {
console.log(`[לקוח] החיבור נסגר (קוד: ${event.code})`);
});
// 5. האזנה לשגיאות
socket.addEventListener('error', (error) => {
console.error('[לקוח] שגיאת WebSocket:', error);
});
HTTP לעומת WebSockets: השוואה מפורטת
| תכונה | HTTP/1.1 | WebSockets |
|---|---|---|
| כיוון תקשורת | חד-כיווני (ביוזמת הלקוח) | דו-כיווני (הן הלקוח והן השרת) |
| מודל חיבור | בקשה-תגובה (זמן קצר) | קבוע ומתמשך (זמן ארוך) |
| תקורה (Overhead) | גבוהה (כותרות נשלחות עם כל בקשה) | נמוכה מאוד (תקורת מסגור מינימלית) |
| מצב (State) | חסר מדינה (Stateless) | בעל מדינה (Stateful, החיבור נשמר) |
| פרוטוקול | http:// או https:// |
ws:// או wss:// |
| הכי מתאים ל- | שליפת מסמכים, APIs של REST | צ’אטים בזמן אמת, דשבורדים, עדכונים חיים |
שיקולי אבטחה עבור WebSockets
מכיוון ש-WebSockets עוקף את ניתוב ה-HTTP הסטנדרטי לאחר לחיצת היד, הוא מציג וקטורי אבטחה ייחודיים:
- השתמש ב-WebSocket Secure (
wss://): הרץ תמיד את ה-WebSockets על גבי TLS/SSL (פורט 443). WSS מצפין את הפריימים, ומונע האזנות סתר ושינוי נתונים על ידי גורמי ביניים. - אימות מקור (Origin Validation): חיבורי WebSockets אינם מוגבלים על ידי Same-Origin Policy (SOP). אמת תמיד את כותרת ה-
Originבשרת במהלך לחיצת היד כדי למנוע גישה לא מורשית. - אימות בחיבור: אמת את המשתמשים לפני יצירת החיבור. הדבר נעשה בדרך כלל על ידי העברת אסימון (כמו JWT) בפרמטרים של הכתובת, או אימות של עוגיות סשן.
- ניקוי קלטים (Sanitization): התייחס לכל הודעה שמתקבלת דרך WebSockets כקלט שאינו אמין. אמת ונקה את הנתונים כדי למנוע התקפות מסוג Cross-Site Scripting (XSS).
סיכום
פרוטוקול WebSockets שינה את אפליקציות האינטרנט הפועלות בזמן אמת על ידי ביטול התקורה של שאילתות HTTP מסורתיות. על ידי שמירה על חיבור TCP יחיד ומתמשך, הוא מאפשר העברת הודעות דו-כיווניות מיידיות, ומניע את הדשבורדים החיים של ימינו, משחקי רשת מרובי משתתפים ואפליקציות צ’אט. הבנת שדרוג ה-HTTP, ארכיטקטורת המסגור ונוהלי האבטחה הקריטיים תבטיח שתבנה שירותים מהירים ומאובטחים בזמן אמת.