WebSocket
WebSocket nima, HTTP Upgrade mexanizmi orqali qanday ochilishi, ws:// va wss:// farqi, matn/binary xabar formatlari, freym tuzilishi va masking, ping/pong, close kodlari, reconnect strategiyasi va event loop darajasida qanday ishlashi.
86-dars
WebSocket
Oddiy HTTP — request-response modeli: klient so'rov yuboradi, server javob qaytaradi, va aloqa yopiladi. Agar serverda yangi ma'lumot paydo bo'lsa (masalan, chatga yangi xabar kelsa), server buni o'zidan klientga yubora olmaydi — chunki HTTP'da faqat klient tomon ulanishni boshlashi mumkin.
Buning eski yechimi — polling: klient har 2 soniyada so'rov yuboraveradi:
setInterval(() => {
fetch('/api/messages').then(res => res.json()).then(showMessages);
}, 2000);
Bu ishlaydi, lekin isrofgarchilik: har bir so'rov — to'liq HTTP header (yuzlab bayt), TCP handshake ehtimoli, va agar 2 soniya orasida hech narsa o'zgarmagan bo'lsa ham — baribir so'rov ketadi.
WebSocket — bu muammoni tubdan yechadi: u bitta doimiy, ikki tomonlama (full-duplex) ulanish ochadi. Ulanish ochilgandan keyin, ham klient, ham server istalgan vaqtda, istalgan tomonga, header'siz, kichik xabar yubora oladi.
1. Qanday ochiladi — HTTP Upgrade mexanizmi
Bu yerda eng muhim texnik nuqta: WebSocket HTTP'ning o'rnini bosmaydi, balki HTTP orqali boshlanadi, keyin protokol almashtiriladi:
const socket = new WebSocket('wss://example.com/chat');
socket.onopen = () => console.log('Ulanish ochildi');
socket.onmessage = (event) => console.log('Xabar keldi:', event.data);
socket.onerror = (error) => console.error('Xato:', error);
socket.onclose = (event) => console.log('Yopildi:', event.code, event.reason);
socket.send('Salom server!');
Orqa fonda, new WebSocket(...) chaqirilganda, brauzer serverga oddiy HTTP so'rov yuboradi, lekin maxsus header'lar bilan:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Server, agar WebSocket'ni qo'llab-quvvatlasa, 101 Switching Protocols statusi bilan javob beradi:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Shu javobdan keyin, xuddi shu TCP ulanish endi HTTP emas — u WebSocket protokoli (RFC 6455) bo'yicha ishlay boshlaydi. TCP socket qayta ochilmaydi — faqat protokol "almashtiriladi" (shuning uchun "Upgrade" deyiladi).
Sec-WebSocket-Key va Sec-WebSocket-Accept — bu xavfsizlik mexanizmi: brauzer tasodifiy key yuboradi, server uni maxsus algoritm (SHA-1 + belgilangan GUID) bilan hisoblab qaytarishi kerak — bu shunchaki HTTP proxy yoki keshlar WebSocket so'rovini tasodifan to'g'ri deb hisoblab, noto'g'ri javob qaytarib yubormasligi uchun tasdiqlash.
ws:// vs wss://
ws:// |
wss:// |
|
|---|---|---|
| Shifrlash | Yo'q (plain text) | TLS orqali (HTTPS kabi) |
| Port (odatiy) | 80 | 443 |
| Ishlatiladigan joy | Faqat lokal test | Production (majburiy — HTTPS sahifadan ws:// ochib bo'lmaydi, browser bloklaydi) |
2. Xabar formatlari — matn va binary
WebSocket ikki turdagi xabarni qo'llab-quvvatlaydi:
socket.send('oddiy matn'); // text frame
socket.send(JSON.stringify({ type: 'msg', text: 'salom' })); // matn sifatida JSON
const buffer = new ArrayBuffer(8);
socket.send(buffer); // binary frame
socket.binaryType = 'blob'; // yoki 'arraybuffer' — kelayotgan binary xabar qaysi turda bo'lishini belgilaydi
Binary format — video/audio stream, fayl uzatish kabi og'ir data uchun ishlatiladi, chunki JSON'ga aylantirish (serialize) qo'shimcha CPU vaqt sarflaydi.
3. Chuqurroq — Frame tuzilishi (protokol darajasida)
WebSocket'da har bir send() chaqiruvi — bu frame (freym) ko'rinishida jismoniy tarmoqqa yuboriladi. Frame'ning boshida kichik binary header bor:
- FIN (1 bit) — bu xabarning oxirgi bo'lagi ekanligi
- Opcode (4 bit) —
0x1= matn,0x2= binary,0x8= close,0x9= ping,0xA= pong - Mask (1 bit) — klientdan kelayotgan freym doim maskalangan bo'lishi SHART
- Payload length — xabar uzunligi
- Masking key (agar mask=1)
- Payload data — asosiy ma'lumot
Bu yerda muhim detal: klientdan serverga ketayotgan har bir freym maskalanishi shart (XOR orqali oddiy shifrlash), lekin serverdan klientga kelayotganida — maskalanmaydi. Sabab — xavfsizlik: agar maskalanmagan bo'lsa, tarmoqdagi proxy'lar WebSocket trafigini oddiy HTTP deb noto'g'ri talqin qilib, kesh serverlarni "zaharlashi" (cache poisoning) mumkin edi — bu real hujum turi bo'lgan va shu sabab maskalash majburiy qilingan.
Ping/Pong freymlari — bu avtomatik, JS darajasida ko'rinmaydi: brauzer va server davriy ravishda bir-biriga "tirikmisan?" (ping) yuboradi, javob (pong) kelmasa — ulanish "o'lik" deb topilib yopiladi. Bu — TCP ulanish "osilib qolgan" holatlarni (masalan, WiFi uzilib qolgan noutbuk) aniqlash uchun kerak.
4. close() — to'g'ri yopish
socket.close(1000, 'Foydalanuvchi chiqdi'); // (code, reason)
| Code | Ma'nosi |
|---|---|
| 1000 | Normal yopilish |
| 1001 | "Going away" — sahifa yopilyapti/navigatsiya |
| 1006 | Anomal yopilish (masalan, tarmoq uzildi — kod avtomatik, qo'lda berilmaydi) |
| 1011 | Serverda kutilmagan xato |
onclose handler'ida event.wasClean — true/false orqali, ulanish to'g'ri protokol bo'yicha (close handshake bilan) yopilganini yoki birdan uzilib qolganini (masalan, internet o'chishi) ajratish mumkin.
5. Reconnect strategiyasi — nega bu qo'lda yozilishi kerak
WebSocket API'da avtomatik qayta ulanish yo'q — bu qasddan shunday, chunki brauzer qanday strategiya (darhol, kutib turib, eksponensial orqaga chekinish) kerakligini bilolmaydi:
function connect() {
const socket = new WebSocket('wss://example.com/chat');
socket.onclose = () => {
setTimeout(connect, 3000); // 3 soniyadan keyin qayta urinish
};
return socket;
}
Real loyihalarda odatda exponential backoff ishlatiladi (1s, 2s, 4s, 8s... server ustiga bosim tushmasligi uchun).
6. V8 va event loop darajasida nima sodir bo'ladi
Bu yerda IndexedDB'dagiga o'xshash tuzilma: WebSocket'ning o'zi V8 ichida amalga oshirilmagan — V8 faqat JS API'ni taqdim etadi, real TCP socket ishi brauzerning network stackida (C++ darajasida, masalan Chromium'da net/websockets/ moduli) bajariladi.
JS: socket.send('salom')
→ V8 bu chaqiruvni C++ binding orqali brauzer network processiga uzatadi
→ Network process TCP socket orqali freymni jismonan yuboradi
→ ...
→ Server javob freymini yuboradi
→ Network process uni qabul qiladi
→ Bu — MAIN THREAD band bo'lsa ham background'da sodir bo'ladi
→ Freym to'liq kelgach, brauzer buni "message" eventi sifatida
V8'ning EVENT LOOP navbatiga (task queue) qo'yadi
→ Main thread bo'shagach, event loop shu taskni oladi
→ socket.onmessage handler chaqiriladi
Bu — nega WebSocket main threadni hech qachon bloklamasligining sababi: haqiqiy tarmoq ishi (TCP, TLS, freym yig'ish) butunlay brauzer process'ida, JS'dan tashqarida ketadi; JS faqat tayyor xabar kelganda, event loop orqali xabardor qilinadi — bu xuddi setTimeout yoki fetchning ishlash mexanizmiga o'xshaydi (Web API → Callback Queue → Event Loop → Call Stack, avvalgi darslarda ko'rilgan asosiy JS mexanizm).
7. WebSocket vs boshqa usullar — qachon nimani tanlash
| Polling | Server-Sent Events (SSE) | WebSocket | |
|---|---|---|---|
| Yo'nalish | Klient so'raydi | Faqat server → klient | Ikki tomonlama |
| Ulanish | Har safar yangi | Bitta doimiy (HTTP asosida) | Bitta doimiy (alohida protokol) |
| Qachon ishlatiladi | Kam o'zgaruvchi data | Live feed, notification (faqat qabul qilish) | Chat, o'yin, real-time hamkorlik |