Seriya: JavaScript Basics Dars 86

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