5.13-bob: Real-time — WebSocket va Socket.io (xonalar, eventlar, namespace)
5-QISM — Node.js Backend · 13-mavzu
1. Kirish va motivatsiya
Hozirgacha qurgan hamma narsamiz — Express 5.6-bob, REST 5.7-bob — so'rov-javob (request-response) modeliga asoslangan: client so'raydi, server javob beradi. Client gapirmaguncha, server jim turadi. Bu — ko'p narsa uchun yetarli (mahsulot ro'yxati, profil). Lekin ba'zi ilovalar boshqacha ishlashi kerak: server o'zi, client so'ramasdan, unga xabar yuborishi kerak.
O'ylab ko'ring: chat ilovasi. Do'stingiz sizga xabar yozdi. Siz brauzerni yangilamasdan, darrov ko'rishingiz kerak. Yoki: bildirishnoma — kimdir postingizga like bosdi, ekranda darrov chiqadi. Yoki: jonli dashboard — birja narxlari, taksi joylashuvi, yetkazib berish holati real vaqtda yangilanadi. Bularning hammasida server o'zi tashabbus ko'rsatib, clientga ma'lumot itaradi (push). Bu — real-time (real vaqtli) aloqa.
REST bilan buni qilib bo'lmaydi (server clientga o'zi so'rovsiz gapirolmaydi). Yechim — WebSocket: client va server o'rtasida doimiy, ikki tomonlama kanal. Va uning ustida ishlashni qulaylashtirgan kutubxona — Socket.io.
O'xshatish 1 (REST vs WebSocket): REST — xat yozish. Siz xat yuborasiz (so'rov), javob xat kelguncha kutasiz. Har safar yangi xat — yangi konvert, yangi marka, yangi yetkazish (har so'rovda yangi ulanish). WebSocket — telefon qo'ng'irog'i: bir marta ulandingiz (qo'ng'iroq), keyin ikki tomon istalgan vaqtda gapiradi, liniya ochiq turadi. Tez, jonli, ikki tomonlama.
O'xshatish 2 (polling vs WebSocket): polling — bolaning mashinada "yetdikmi? yetdikmi? yetdikmi?" deb har daqiqa so'rashi (client har necha sekundda serverdan so'raydi — isrof). WebSocket — ota "yetganda aytaman" deyishi (server tayyor bo'lganda o'zi xabar beradi).
Nega muhim?
- Zamonaviy interaktiv ilovalar — chat, bildirishnoma, jonli yangilanish, hamkorlik (Google Docs), o'yin.
- REST yetmaydi — server o'zi push qila olmaydi; real-time uchun WebSocket.
- Stack talabi — Najot Ta'lim va sizning stack'ingizda Socket.io bor (NestJS gateway — 8.21 bilan ham).
- UX — jonli, darrov; sahifa yangilashsiz.
2. Nazariya — chuqur tushuntirish
2.1. So'rov-javob modelining cheklovi
Klassik HTTP (0.4, 5.5) bir tomonlama tashabbus: faqat client so'rov boshlaydi, server faqat javob beradi. Server "o'zidan" clientga "hey, yangi xabar bor" deya olmaydi — chunki ulanish so'rovdan keyin yopiladi (0.4: HTTP stateless).
HTTP (REST):
Client ──── so'rov ────▶ Server
Client ◀─── javob ───── Server (ulanish yopiladi)
...
Server'da yangilik bo'ldi lekin clientga AYTOLMAYDI (kanal yopiq)Demak, real-time uchun boshqa yondashuv kerak: ulanish ochiq qolsin va server xohlagan vaqt gapira olsin.
2.2. Real-time'ning eski yechimlari (polling) — nega yomon
WebSocket'dan oldin real-time'ga urinishlar bo'lgan. Ularni bilish — WebSocket nega kerakligini tushuntiradi:
1. Short polling (qisqa so'rash): client har necha sekundda "yangilik bormi?" deb so'raydi:
Client: "yangilik bormi?" Server: "yo'q" (har 3 sekundda)
Client: "yangilik bormi?" Server: "yo'q"
Client: "yangilik bormi?" Server: "ha, mana!" (nihoyat)Muammo: ko'p isrof so'rov (99% "yo'q" javobi); kechikish (3 sekundgacha eskirgan); server yuki (har client doim so'raydi). Tasavvur qiling: 10000 client har 3 sekundda so'rasa — server portlaydi.
2. Long polling (uzun so'rash): client so'raydi, server javobni yangilik bo'lguncha ushlab turadi, keyin javob beradi; client darrov yana so'raydi:
Client: "yangilik bormi?" Server ... (ushlab turadi, kutadi) ...
Server: "ha!" (yangilik bo'lganda javob)
Client darrov yana so'raydi ...Yaxshiroq (kamroq isrof), lekin baribir har xabar uchun yangi HTTP so'rov; murakkab; to'liq ikki tomonlama emas. Socket.io buni fallback sifatida ishlatadi 2.14-bob.
2.3. WebSocket — haqiqiy yechim (protokol)
WebSocket — HTML5 bilan kelgan protokol (HTTP'dan alohida, lekin u bilan boshlanadi): client va server o'rtasida bitta, doimiy, ikki tomonlama (full-duplex) TCP 0.4-bob ulanish. Bir marta o'rnatilgach, ikki tomon ham istalgan vaqt ma'lumot yuboradi — yangi so'rovsiz:
WebSocket:
Client ══════ doimiy ochiq kanal ══════ Server
◀──────── ikki tomonlama ────────▶
Client xohlasa yuboradi
Server xohlasa yuboradi (so'rov kutmasdan!)Manzil: WebSocket
ws://(yoki xavfsizwss://— 14) protokolidan foydalanadi (HTTP'ninghttp:///https://o'rniga).
2.4. WebSocket qanday boshlanadi (handshake — under the hood)
WebSocket ulanishi HTTP so'rov bilan boshlanadi (shuning uchun mavjud infratuzilma — port 80/443 — ishlaydi), keyin protokol "yangilanadi" (upgrade):
1. Client HTTP so'rov yuboradi, maxsus header bilan:
GET /socket HTTP/1.1
Upgrade: websocket "protokolni WebSocketga o'zgartir"
Connection: Upgrade
Sec-WebSocket-Key: <kalit>
2. Server rozi bo'lsa, javob:
HTTP/1.1 101 Switching Protocols 101 status 5.7-bob
Upgrade: websocket
3. Endi ulanish WebSocketga "aylandi" — doimiy, ikki tomonlama.
HTTP tugadi, WebSocket boshlandi (bitta TCP ulanish ustida).Nega HTTP bilan boshlanadi: mavjud portlar (80/443), proxy, firewall 0.4-bob WebSocket'ni ham o'tkazadi (HTTP kabi ko'rinadi boshida).
101 Switching Protocols— "kelishdik, endi WebSocket" degani.
WebSocket vs SSE vs polling — qaysi birini tanlash? Har real-time'ga WebSocket shart emas. Uchta asosiy yondashuv bor:
Texnologiya Yo'nalish Ulanish Qachon Polling (2.2) client server har so'rovda yangi sodda, kam yangilanish; eski brauzer SSE (Server-Sent Events) faqat server client bitta doimiy HTTP server bir tomonlama push (jonli lenta, narx, log oqimi) WebSocket ikki tomonlama bitta doimiy TCP chat, o'yin, hamkorlik (ikki tomon gapiradi) SSE — HTTP ustidagi standart (
EventSource,text/event-stream): server clientga uzluksiz xabar oqimi yuboradi, lekin client SSE orqali javob yubora olmaydi (faqat oddiy HTTP so'rov bilan). Avtomatik qayta ulanish o'zida bor. Bir tomonlama yetadigan joyda (bildirishnoma, jonli dashboard) SSE — yengilroq tanlov. Ikki tomon faol gaplashganda (chat, typing indikatori) — WebSocket. Socket.io ham, asosan, ikki tomonlama holatlar uchun.
2.5. Socket.io nima va nega kerak (raw WebSocket ustida)
Toza WebSocket (ws paketi) — past darajali. U bilan ishlashda ko'p narsani qo'lda qilish kerak: avtomatik qayta ulanish (internet uzilsa), eski brauzer qo'llab-quvvatlashi (fallback), xonalar, eventlar. Socket.io — WebSocket ustiga qurilgan kutubxona, bularning hammasini tayyor beradi (socket.io docs):
Socket.io = WebSocket + qo'shimcha qulayliklar:
Avtomatik qayta ulanish (internet uzilib-ulansa)
Fallback (WebSocket bloklansa — long-polling, 2.14)
Event-based API (emit/on — 2.6)
Rooms (xonalar — 2.8) va Namespaces 2.9-bob
Broadcasting (ko'pga yuborish — 2.7)
Acknowledgements (tasdiqlash — 2.10)
Redis adapter (masshtablash — 2.16)Muhim: Socket.io — WebSocket emas (uning ustidagi protokol). Socket.io client faqat Socket.io server bilan gaplashadi (toza WebSocket client bilan emas). Lekin u real-time ishini ancha osonlashtiradi — shuning uchun amaliyotda keng ishlatiladi (stack'ingizda ham).
2.6. Eventlar — Socket.io'ning yuragi (emit / on)
Socket.io event-based (hodisaga asoslangan — 2.16-JS: EventEmitter ruhida): bir tomon event yuboradi (emit), ikkinchi tomon uni tinglaydi (on). Event — nom + ma'lumot:
// Yuborish (emit): event nomi + ma'lumot
socket.emit("xabar", { matn: "Salom", kim: "Ali" });
// Tinglash (on): event nomi + callback (2.3-JS)
socket.on("xabar", (data) => {
console.log(data.matn); // "Salom"
});Nega event: REST'da URL/metod 5.7-bob bilan ishladingiz; Socket.io'da event nomi bilan.
"xabar","yozmoqda","yangi-buyurtma"— o'zingiz nomlaysiz. Bu — moslashuvchan, tabiiy (real hodisalar nomi).
2.7. Kimga yuborish — emit cheatsheet
Eng muhim tushunchalardan biri: eventni kimga yuborish? Socket.io ko'p variant beradi (socket.io emit cheatsheet):
socket.emit(...) FAQAT shu clientga (o'ziga)
io.emit(...) BARCHA ulangan clientlarga
socket.broadcast.emit(...) o'zidan TASHQARI hammaga
io.to("xona").emit(...) "xona"dagi hammaga 2.8-bob
socket.to("xona").emit(...) "xona"dagi hammaga (o'zidan tashqari)
io.to("a").to("b").emit(...) "a" VA "b" xonalardagilarga
io.except("xona").emit(...) "xona"dan tashqari hammaga
socket.to(socketId).emit(...) muayyan bitta clientga (lichka — 2.8) ┌─────────────────── io (barcha) ───────────────────┐
│ socket (o'zi) broadcast (o'zidan tashqari) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Client A │ │ Client B │ │ Client C │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────────────────────────────────┘
A'dan socket.broadcast.emit B va C oladi (A olmaydi)2.8. Rooms (xonalar) — eng muhim tushuncha
Room (xona) — clientlarni guruhlash mexanizmi. Bir client bir yoki bir nechta xonaga "kirishi" (join) mumkin; keyin butun xonaga xabar yuboriladi (socket.io rooms). Bu — chat guruhlari, o'yin xonalari, alohida foydalanuvchi kanallari uchun ideal:
socket.join("xona-7"); // xonaga kirish
io.to("xona-7").emit("xabar", data); // shu xonadagi HAMMAGA
socket.leave("xona-7"); // xonadan chiqishIchki ishlashi (under the hood): Socket.io ikki Map (2.9-JS) saqlaydi — socket xonalari, va xona socketlari (socket.io docs). Broadcast'da xona socketlar Map'i ishlatiladi.
Real misol (chat):
┌─ "guruh-matematika" xonasi ─┐ ┌─ "guruh-fizika" xonasi ─┐
│ Ali, Vali, Hasan │ │ Olim, Salim │
└─────────────────────────────┘ └─────────────────────────┘
io.to("guruh-matematika").emit("xabar") faqat Ali, Vali, Hasan oladi
(Olim, Salim olmaydi — boshqa xonada)Muhim nuans: har client avtomatik o'z ID'si nomli xonaga kiradi (
socket.id). Shuning uchunio.to(socketId).emit(...)— muayyan bitta clientga (shaxsiy xabar) ishlaydi. Foydalanuvchi ID'si bo'yicha xona (user:42) — o'sha foydalanuvchining barcha qurilmalariga (telefon+laptop) yuborish uchun qulay.
2.9. Namespaces (nom maydonlari) — mantiqiy ajratish
Namespace — bitta ulanish ustida ilovani mantiqiy bo'lish (socket.io docs). Har namespace — alohida "kanal" o'z eventlari, xonalari, middleware'i bilan:
const chatNs = io.of("/chat"); // /chat namespace
const adminNs = io.of("/admin"); // /admin namespace (alohida)
chatNs.on("connection", (socket) => {...}); // faqat /chat clientlari
adminNs.on("connection", (socket) => {...}); // faqat /admin clientlari Bitta server, bitta ulanish, lekin mantiqiy bo'lingan:
io.of("/") asosiy (default) namespace
io.of("/chat") chat mantig'i
io.of("/admin") admin paneli (faqat ruxsatli — 2.13)
io.of("/orders") buyurtma yangilanishlariNamespace vs Room farqi: Namespace — kod darajasida ajratish (turli xil funksiya: chat vs admin), turli middleware. Room — namespace ichidagi dinamik guruhlash (qaysi chat guruhi). Namespace — "bo'lim"; room — "stol". Odatda bitta-ikkita namespace yetadi; rooms — ko'p, dinamik.
2.10. Acknowledgements (tasdiqlash) — javob qaytarish
Ba'zan eventni yuborib, ikkinchi tomon uni oldimi bilish kerak (xabar yetdimi?). Acknowledgement — callback orqali javob (socket.io docs):
// Yuboruvchi: oxirgi argument — callback (javobni kutadi)
socket.emit("xabar-yubor", { matn: "Salom" }, (javob) => {
console.log("Server javobi:", javob); // server qaytargan natija
});
// Qabul qiluvchi: callback'ni chaqirib javob beradi
socket.on("xabar-yubor", (data, callback) => {
// ... saqlash ...
callback({ status: "ok", id: 123 }); // yuboruvchiga qaytadi
});Timeout bilan (4.5.0+): javob kelmasligi mumkin (tarmoq).
socket.timeout(5000).emit(...)— 5 sekund kutadi, kelmasa xato (socket.io). Bu — "xabar yuborildi/yetdi" belgisi (chat'dagi ) uchun ishlatiladi.
2.11. Broadcasting modifikatorlari (to, except, volatile)
Yuborishni nozik boshqarish (socket.io broadcasting):
io.to("xona1").except("xona2").emit(...) // xona1, lekin xona2'dan tashqari
socket.volatile.emit("narx", data); // yetmasa — mayli (tashlab ketsa bo'ladi)
io.timeout(5000).to("xona").emit(..., cb); // xona + tasdiqlash + timeout (2.10)
volatile— agar client band/uzilgan bo'lsa, xabar tashlab yuboriladi (navbatga qo'yilmaydi). Tez-tez yangilanadigan, eskirib qoladigan ma'lumot uchun (masalan jonli koordinata, o'yin holati) — eski kadrni saqlashning ma'nosi yo'q.
2.12. Connection lifecycle (ulanish hayot sikli)
Har client ulanganda/uzilganda — maxsus eventlar:
connection — yangi client ulandi (server tomonda io.on("connection"))
disconnect — client uzildi (internet, brauzer yopildi)
reconnect — Socket.io avtomatik qayta ulandi (client tomonda)
connect_error — ulanishda xato (auth rad etdi — 2.13)io.on("connection", (socket) => {
console.log("Ulandi:", socket.id);
socket.on("disconnect", (reason) => { // uzilganda (2.12)
console.log("Uzildi:", socket.id, reason); // tozalash (online ro'yxatdan o'chir)
});
});
socket.id— har ulanishning noyob ID'si. U doimiy emas — qayta ulanganda o'zgaradi. Shuning uchun foydalanuvchini ID'si bo'yicha emas, o'z user ID'si bo'yicha kuzating (DB/auth — 2.13, 2.18).
Heartbeat (ping/pong) — uzilishni qanday sezadi: TCP ulanish "jim" uzilishi mumkin (Wi-Fi o'chdi, tunnel yopildi) — bunda disconnect darrov chiqmaydi, ulanish "o'lik" bo'lsa-da ochiq ko'rinadi. Socket.io buni ping/pong mexanizmi bilan aniqlaydi: server muntazam ping yuboradi, client pong bilan javob qaytaradi. Belgilangan vaqtda javob kelmasa — ulanish uzilgan deb hisoblanadi (socket.io connection-state):
Server ──ping──▶ Client (har pingInterval, masalan 25s)
Server ◀──pong── Client (client tirik — javob beradi)
...
Server ──ping──▶ (pingTimeout ichida pong YO'Q, masalan 20s)
Server ulanishni uzilgan deb belgilaydi disconnect (reason: "ping timeout")const io = new Server(httpServer, {
pingInterval: 25000, // har 25s ping yuboradi
pingTimeout: 20000, // pong shu vaqtda kelmasa — uzilgan deb hisoblaydi
});Ping/pong — Socket.io o'zi boshqaradi (qo'lda yozish shart emas). Lekin uni bilish muhim:
disconnectba'zan darhol emas,pingTimeoutdan keyin chiqadi. Shuning uchun "online" holatni shu kechikishni hisobga olib ko'rsating (Misol 8).disconnectsababi (reason) —"ping timeout","transport close"va h.k. — muammoni tashxislashda yordam beradi 2.12-bob.
2.13. Middleware va autentifikatsiya (xavfsizlik — 14)
REST'da auth — har so'rovda token 5.15-bob. Socket.io'da — ulanishdan oldin middleware bilan, bir marta (socket.io middlewares, 14):
// Socket.io middleware — ulanish o'rnatilishidan OLDIN ishlaydi
io.use((socket, next) => {
const token = socket.handshake.auth.token; // client yuborgan token (2.4: handshake)
if (!token) return next(new Error("Token yo'q")); // ulanishni RAD et
try {
socket.user = jwt.verify(token, SECRET); // tekshiring, socketga biriktiring (5.15)
next(); // ruxsat — ulanadi
} catch {
next(new Error("Token noto'g'ri")); // rad — connect_error (2.12)
}
});Real-time'da ham auth MAJBURIY (14): WebSocket ham REST kabi hujum nishoni. Middleware'da JWT 5.15-bob tekshiring;
socket.userni biriktiring; keyin eventlardasocket.userni ishlating.next()har holda chaqirilsin (ruxsat — argumentsiz; rad —next(error)). Middleware ishlaganda socket hali ulanmagan — rad etilsadisconnectchiqmaydi (docs).
2.14. Transports va fallback (sticky sessions)
Socket.io ikki transport ishlatadi (socket.io):
websocket — asosiy (tez, bitta TCP ulanish — 2.3)
polling — fallback (HTTP long-polling — 2.2; WebSocket bloklansa)Socket.io avval polling bilan ulanadi, keyin imkon bo'lsa WebSocket'ga "ko'tariladi" (upgrade).
Sticky sessions (muhim — 2.16): bir nechta server bo'lsa (load balancer — 10.2), bitta client'ning barcha so'rovlari bir serverga borishi kerak (HTTP long-polling bir nechta so'rov yuboradi). Bu — sticky session. WebSocket (bitta ulanish) yoki Redis (shared state — 2.16) bilan hal qilinadi. Aks holda — ulanish uziladi.
2.15. Express/HTTP server bilan integratsiya
Socket.io mavjud HTTP server 5.5-bob ustiga o'rnatiladi — Express 5.6-bob bilan bir portda birga ishlaydi:
import { createServer } from "node:http"; // (5.5)
import express from "express";
import { Server } from "socket.io";
const app = express(); // Express (5.6)
const httpServer = createServer(app); // HTTP server (5.5)
const io = new Server(httpServer, { cors: {...} }); // Socket.io shu server ustida
httpServer.listen(3000); // bitta port — REST + WebSocketExpress REST endpoint'lari (
/api/...) va Socket.io real-time — bitta serverda birga. REST'dan turib ham event yuborish mumkin (io.emit— masalan yangi buyurtma kelganda — 2.18).
2.16. Masshtablash — Redis adapter (horizontal scaling)
Bitta server cheklangan (CPU/RAM — 0.1). Yuk oshganda — bir nechta server (horizontal — 9.9). Lekin muammo: A serverga ulangan client va B serverga ulangan client bir-birini ko'rmaydi (har server o'z xotirasidagi clientlarni biladi):
Muammo:
Server A: Ali, Vali ulangan
Server B: Hasan ulangan
Ali "xabar" yubordi faqat Server A clientlari (Vali) oladi
Hasan (Server B) OLMAYDI! (boshqa server)Yechim — Redis adapter (5.21: Redis Pub/Sub): serverlar Redis orqali bir-biriga xabar uzatadi (socket.io redis-adapter):
Redis (Pub/Sub) — barcha serverlarni bog'laydi
▲ ▲
Server A Server B
(Ali,Vali) (Hasan)
Ali xabar Server A Redis'ga e'lon Server B oladi Hasan oladi import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis"; // (5.21)
const pub = createClient({ url: process.env.REDIS_URL }); // (5.8)
const sub = pub.duplicate();
await Promise.all([pub.connect(), sub.connect()]);
io.adapter(createAdapter(pub, sub)); // endi serverlar bog'langanBu — production'da (10) real-time'ni masshtablashning standart usuli (Redis — 5.21'da chuqur). 100K+ ulanish uchun Redis adapter + namespace partitioning (manbalar).
2.17. Xavfsizlik (14) — real-time uchun
Real-time xavfsizligi (14):
- Auth — middleware'da JWT (2.13, 5.15); ulanishni tekshiring.
- CORS — faqat ruxsatli domenlar (
cors.origin— 5.20, 14). - Validatsiya — kelgan event ma'lumotini tekshiring (Zod — 5.9); clientga ishonmang (14).
- Avtorizatsiya — foydalanuvchi shu xonaga/amalga haqlimi (RBAC — 5.17).
- Rate limiting — bir client juda ko'p event yubormasin (spam/DoS — 5.20).
wss://(shifrlangan — 14) production'da.
2.18. Real hayotdagi qo'llanishlar (use cases)
Chat (1:1, guruh) — rooms (har suhbat — xona); typing indikatori
Bildirishnoma — user:ID xonasi; like/comment/follow real-time
Jonli dashboard — birja, analitika, yetkazib berish kuzatuvi
E-commerce — yangi buyurtma adminga; stok yangilanishi
Hamkorlik (Google Docs) — bir hujjatni birga tahrirlash
Onlayn o'yin — o'yinchilar holati, harakatlar
Taksi/yetkazib berish — haydovchi joylashuvi real vaqtdaBu bobdagi misollar shu stsenariylarga asoslanadi (chat, bildirishnoma, e-commerce buyurtma).
3. Sintaksis — tez ma'lumotnoma
// SERVER
import { Server } from "socket.io";
const io = new Server(httpServer, { cors: { origin: "..." } }); // (2.15)
io.use((socket, next) => {...}); // middleware/auth (2.13)
io.on("connection", (socket) => {
socket.emit("event", data); // o'ziga (2.7)
socket.broadcast.emit("event", data); // o'zidan tashqari hammaga
io.emit("event", data); // hammaga
socket.join("xona"); socket.leave("xona"); // room (2.8)
io.to("xona").emit("event", data); // xonaga
socket.on("mijoz-event", (data, cb) => cb(...)); // tinglash + ack (2.10)
socket.on("disconnect", () => {...}); // uzilish (2.12)
});
const ns = io.of("/chat"); // namespace (2.9)
// CLIENT (brauzer)
import { io } from "socket.io-client";
const socket = io("http://localhost:3000", { auth: { token } }); // (2.13)
socket.emit("event", data);
socket.on("event", (data) => {...});4. Batafsil kod namunalari
Misol 1 — Eng oddiy server + client (2.6, 2.15)
// server.js
import { createServer } from "node:http"; // (5.5)
import { Server } from "socket.io";
const httpServer = createServer(); // HTTP server (5.5)
const io = new Server(httpServer, {
cors: { origin: "http://localhost:5173" }, // frontend manzili (2.17, 14)
});
// Har client ulanganda (2.12)
io.on("connection", (socket) => {
console.log(" Ulandi:", socket.id); // noyob ID (2.12)
// Client "salom" yuborsa tinglaymiz (2.6)
socket.on("salom", (matn) => {
console.log("Client:", matn);
socket.emit("javob", `Server: "${matn}" qabul qilindi`); // o'ziga javob (2.7)
});
socket.on("disconnect", () => console.log(" Uzildi:", socket.id)); // (2.12)
});
httpServer.listen(3000, () => console.log("Socket.io 3000-portda"));// client.js (brauzer/Node)
import { io } from "socket.io-client";
const socket = io("http://localhost:3000");
socket.on("connect", () => { // ulanganda (2.12)
console.log("Ulandim, ID:", socket.id);
socket.emit("salom", "Assalomu alaykum"); // serverga yuborish (2.6)
});
socket.on("javob", (data) => console.log(data)); // server javobini tinglashMisol 2 — Custom eventlar (bir nechta — 2.6)
io.on("connection", (socket) => {
// Turli eventlar — har biri o'z nomi va ma'lumoti bilan (2.6)
socket.on("xabar:yuborish", (data) => {
console.log(`${data.kim}: ${data.matn}`);
});
socket.on("foydalanuvchi:yozmoqda", (kim) => {
socket.broadcast.emit("foydalanuvchi:yozmoqda", kim); // boshqalarga "X yozmoqda" (2.7)
});
socket.on("reaksiya", ({ postId, emoji }) => {
io.emit("reaksiya:yangi", { postId, emoji }); // hammaga (2.7)
});
});
// Event nomlarida ":" bilan guruhlash — odatiy konvensiya (xabar:yuborish, xabar:o'chirish)Misol 3 — Chat xonalar bilan (rooms — 2.8)
io.on("connection", (socket) => {
// Xonaga qo'shilish (chat guruhi — 2.8)
socket.on("xona:kirish", (xonaNomi) => {
socket.join(xonaNomi); // xonaga kiring (2.8)
// O'sha xonadagi BOSHQALARGA xabar bering (o'zidan tashqari — 2.7)
socket.to(xonaNomi).emit("tizim", `Yangi a'zo qo'shildi`);
socket.emit("tizim", `"${xonaNomi}" xonasiga kirdingiz`); // o'ziga
});
// Xonaga xabar yuborish (2.8)
socket.on("xona:xabar", ({ xona, matn, kim }) => {
// Faqat shu xonadagilarga (boshqa xonalar OLMAYDI — 2.8)
io.to(xona).emit("xona:xabar", { matn, kim, vaqt: Date.now() });
});
// Xonadan chiqish
socket.on("xona:chiqish", (xona) => {
socket.leave(xona); // (2.8)
socket.to(xona).emit("tizim", "A'zo chiqdi");
});
});
// Real chat: har guruh/suhbat — alohida xona; xabar faqat o'z xonasiga boradiMisol 4 — emit variantlari amalda (cheatsheet — 2.7)
io.on("connection", (socket) => {
socket.emit("a", data); // 1. faqat shu clientga (2.7)
io.emit("b", data); // 2. BARCHA clientlarga
socket.broadcast.emit("c", data); // 3. o'zidan tashqari hammaga
io.to("xona7").emit("d", data); // 4. "xona7"dagi hammaga (2.8)
socket.to("xona7").emit("e", data); // 5. "xona7" (o'zidan tashqari)
io.to("a").to("b").emit("f", data); // 6. "a" va "b" xonalarga
io.except("vip").emit("g", data); // 7. "vip"dan tashqari hammaga
io.to(targetSocketId).emit("h", data); // 8. muayyan bitta clientga (lichka — 2.8)
});Misol 5 — Namespaces (mantiqiy ajratish — 2.9)
// Asosiy namespace (default)
io.on("connection", (socket) => { /* umumiy */ });
// Chat namespace — o'z mantig'i (2.9)
const chat = io.of("/chat");
chat.on("connection", (socket) => {
socket.on("xabar", (data) => chat.emit("xabar", data)); // faqat /chat clientlari
});
// Admin namespace — alohida middleware (faqat admin — 2.13)
const admin = io.of("/admin");
admin.use((socket, next) => {
if (socket.handshake.auth.role === "admin") next();
else next(new Error("Faqat admin")); // rad (2.13, 14)
});
admin.on("connection", (socket) => {
socket.on("statistika", () => socket.emit("statistika", getStats()));
});
// Client: io("http://localhost:3000/chat"), io("http://localhost:3000/admin")Misol 6 — Acknowledgements + timeout (tasdiqlash — 2.10)
// SERVER — callback bilan javob (2.10)
io.on("connection", (socket) => {
socket.on("xabar:saqlash", async (data, callback) => {
try {
const saqlangan = await Message.create(data); // DB (6)
callback({ status: "ok", id: saqlangan.id }); // yuboruvchiga javob
} catch (err) {
callback({ status: "error", xato: err.message }); // xato (5.10)
}
});
});
// CLIENT — javobni kutish (timeout bilan — 2.10)
socket.timeout(5000).emit("xabar:saqlash", { matn: "Salom" }, (err, javob) => {
if (err) {
console.log("Server javob bermadi (timeout)"); // 5 sekund o'tdi
} else if (javob.status === "ok") {
console.log("Saqlandi, ID:", javob.id); // (yetdi belgisi)
}
});Misol 7 — JWT autentifikatsiya middleware (xavfsizlik — 2.13, 14)
import jwt from "jsonwebtoken"; // (5.15)
import { config } from "./config/index.js"; // (5.8)
// Ulanishdan OLDIN — token tekshiring (2.13)
io.use((socket, next) => {
const token = socket.handshake.auth.token; // client yuborgan (2.4)
if (!token) return next(new Error("AUTH_KERAK"));
try {
const payload = jwt.verify(token, config.jwt.secret); // (5.15)
socket.user = { id: payload.sub, rol: payload.rol }; // socketga biriktiring
socket.join(`user:${payload.sub}`); // shaxsiy xona 2.8-bob — ko'p qurilma
next(); // ruxsat
} catch {
next(new Error("TOKEN_NOTOGRI")); // rad (2.12: connect_error)
}
});
io.on("connection", (socket) => {
console.log("Auth'dan o'tdi:", socket.user.id); // endi har eventda socket.user bor
socket.on("xabar", (data) => {
// socket.user.id — ishonchli (token'dan); clientga ishonmang (14)
io.emit("xabar", { ...data, kim: socket.user.id });
});
});
// CLIENT: io(url, { auth: { token: localStorage.getItem("token") } })Misol 8 — Online foydalanuvchilar (lifecycle — 2.12)
const onlayn = new Map(); // userId socketId(lar) (2.9-JS)
io.on("connection", (socket) => {
const userId = socket.user.id; // auth'dan (2.13)
onlayn.set(userId, socket.id);
io.emit("onlayn:royxat", [...onlayn.keys()]); // hammaga yangi ro'yxat (2.7)
socket.on("disconnect", () => { // uzilganda (2.12)
onlayn.delete(userId);
io.emit("onlayn:royxat", [...onlayn.keys()]); // yangilangan ro'yxat
// "Oxirgi marta onlayn" vaqtini DB'ga yozish mumkin (6)
});
});
// Production'da onlayn holatni Redis'da saqlang (ko'p server — 2.16, 5.21)Misol 9 — Express bilan to'liq integratsiya (2.15)
// server.js
import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";
import { config } from "./config/index.js";
const app = express();
app.use(express.json()); // REST (5.6)
app.get("/api/health", (req, res) => res.json({ ok: true })); // REST endpoint
const httpServer = createServer(app); // Express + http (5.5, 2.15)
const io = new Server(httpServer, {
cors: { origin: config.clientUrl, credentials: true }, // (2.17, 14)
});
// io'ni boshqa fayllarda ishlatish uchun eksport (REST'dan event yuborish — 2.18)
export { io };
io.on("connection", (socket) => { /* real-time */ });
httpServer.listen(config.port, () =>
console.log(`REST + WebSocket: ${config.port}-port`) // bitta port (2.15)
);Misol 10 — REST'dan real-time event (e-commerce — 2.18)
import { io } from "../server.js"; // (Misol 9)
// REST endpoint: yangi buyurtma (5.6, 5.7)
app.post("/api/orders", async (req, res) => {
const buyurtma = await Order.create(req.body); // DB (6)
res.status(201).json({ success: true, data: buyurtma }); // REST javobi (5.7)
// VA real-time: adminlarga darrov bildirishnoma (2.18)
io.to("admins").emit("buyurtma:yangi", { // admin xonasiga (2.8)
id: buyurtma.id,
summa: buyurtma.total,
mijoz: buyurtma.userId,
});
// Mijozning shaxsiy xonasiga (2.8)
io.to(`user:${buyurtma.userId}`).emit("buyurtma:tasdiq", { id: buyurtma.id });
});
// REST va real-time birga: buyurtma saqlandi (REST) + admin darrov ko'rdi (WebSocket)Misol 11 — Redis adapter (masshtablash — 2.16)
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis"; // (5.21)
import { config } from "./config/index.js";
const io = new Server(httpServer, { cors: {...} });
// Redis Pub/Sub — bir nechta serverni bog'laydi (2.16)
const pubClient = createClient({ url: config.redis.url }); // (5.8, 5.21)
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient)); // endi serverlar bir-birini ko'radi
// Endi io.emit/io.to BARCHA serverlardagi clientlarga yetadi (2.16)
io.on("connection", (socket) => {
socket.on("global:xabar", (data) => io.emit("global:xabar", data)); // hamma serverga
});
// PM2/Docker 10.7-bob bilan bir nechta instansiya + sticky session (2.14)Misol 12 — Typing indikatori + shaxsiy xabar (chat — 2.7, 2.8)
io.on("connection", (socket) => {
const myId = socket.user.id; // (2.13)
// "Yozmoqda..." indikatori (suhbat xonasiga, o'zidan tashqari — 2.7)
socket.on("yozmoqda", ({ suhbatId }) => {
socket.to(`suhbat:${suhbatId}`).emit("yozmoqda", { kim: myId });
});
socket.on("toxtadi", ({ suhbatId }) => {
socket.to(`suhbat:${suhbatId}`).emit("toxtadi", { kim: myId });
});
// Shaxsiy xabar (lichka) — qabul qiluvchining shaxsiy xonasiga (2.8)
socket.on("lichka", async ({ kimga, matn }) => {
const xabar = await Message.create({ from: myId, to: kimga, matn }); // DB (6)
io.to(`user:${kimga}`).emit("lichka", xabar); // qabul qiluvchiga (barcha qurilma)
socket.emit("lichka:yuborildi", xabar); // o'ziga tasdiq
});
});5. To'g'ri va noto'g'ri holatlar
1) Real-time'ni autentifikatsiyasiz qoldirish
// har kim ulanib, har eventni tinglaydi (14, 2.13)
io.on("connection", (socket) => { socket.on("xabar", ...) });
// middleware'da JWT tekshiring (2.13)
io.use((socket, next) => { /* token verify */ });2) socket.id'ni doimiy identifikator deb ishlatish
// socket.id qayta ulanganda o'zgaradi (2.12)
onlineUsers.set(socket.id, ...);
// user ID (auth'dan — barqaror)
onlineUsers.set(socket.user.id, ...);3) Hamma narsani io.emit (barchaga) yuborish
// shaxsiy xabar HAMMAGA ketdi (maxfiylik buzilishi! — 14, 2.7)
io.emit("lichka", xabar);
// faqat kerakli xona/clientga (2.8)
io.to(`user:${kimga}`).emit("lichka", xabar);4) Kelgan event ma'lumotiga ishonish
// clientga ishonib (kim: data.kim — soxta bo'lishi mumkin — 14)
socket.on("xabar", (data) => io.emit("xabar", { kim: data.kim }));
// socket.user (token'dan — ishonchli — 2.13); ma'lumotni validatsiya (5.9)
socket.on("xabar", (data) => io.emit("xabar", { kim: socket.user.id, matn: data.matn }));5) Bir nechta serverda Redis adaptersiz
2 server, Redis yo'q A va B clientlari bir-birini ko'rmaydi (2.16)
Redis adapter + sticky session (2.14, 2.16)6) CORS ochiq qoldirish
// har domen ulanadi (14)
new Server(httpServer, { cors: { origin: "*" } });
// faqat ruxsatli domen (2.17)
new Server(httpServer, { cors: { origin: config.clientUrl } });6. Keng tarqalgan xatolar va yechimlari
Xato 1 — CORS error (client ulanmaydi)
Sababi: server CORS sozlanmagan 2.17-bob. Yechimi: new Server(httpServer, { cors: { origin: "<frontend-url>" } }); credentials kerak bo'lsa credentials: true.
Xato 2 — Client ulanadi, lekin event kelmaydi
Sababi: event nomi mos emas (emit("xabar") vs on("Xabar")); yoki noto'g'ri target (socket.emit o'rniga broadcast). Yechimi: nomlarni aniq moslash (katta-kichik harf); to'g'ri emit varianti 2.7-bob.
Xato 3 — socket.id har safar o'zgaradi
Sababi: qayta ulanish — yangi ID 2.12-bob. Yechimi: foydalanuvchini user ID bilan kuzating; user ID xonasiga join (2.8, 2.13).
Xato 4 — Bir nechta serverda xabar yetmaydi
Sababi: Redis adapter yo'q 2.16-bob. Yechimi: @socket.io/redis-adapter (Misol 11); sticky session 2.14-bob.
Xato 5 — WebSocket connection failed / polling'da qotib qoladi
Sababi: proxy/nginx WebSocket'ni o'tkazmayapti 10.2-bob, yoki sticky session yo'q 2.14-bob. Yechimi: nginx'da Upgrade/Connection header'lari 10.2-bob; sticky session.
Xato 6 — Xabar dublikat (ikki marta keladi)
Sababi: on bir necha marta ro'yxatdan o'tdi (komponent qayta render — 11), yoki o'ziga ham, broadcast'ga ham yubordi. Yechimi: listener'ni bir marta; client'da off bilan tozalash (React'da useEffect cleanup — 11.5).
Xato 7 — Xotira oshib ketadi (memory leak)
Sababi: uzilgan clientlar tozalanmagan (Map'dan o'chirilmagan — 2.12). Yechimi: disconnectda tozalang (Misol 8); Redis'da saqlang 2.16-bob.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- HTTP server 5.5-bob: Socket.io uning ustida.
- Express 5.6-bob: bir portda REST + WebSocket 2.15-bob; REST'dan event 2.18-bob.
- EventEmitter (5.4, 2.16-JS): Socket.io event modeli shu ruhda.
- Auth/JWT 5.15-bob: handshake middleware 2.13-bob.
- RBAC 5.17-bob: namespace/event avtorizatsiyasi.
- Validatsiya 5.9-bob: kelgan event ma'lumoti.
- Redis 5.21-bob: adapter — masshtablash 2.16-bob.
- CORS/xavfsizlik (5.20, 14): origin, rate limit.
- DB (6): xabarlarni saqlash; online holat.
- React (11): socket.io-client, useEffect bilan 11.5-bob.
- NestJS 8.21-bob: WebSocket Gateway — shu g'oya, dekorator bilan.
- DevOps (10.2, 10.7): nginx WebSocket proxy, PM2 cluster, sticky session.
8. Eng yaxshi amaliyotlar (best practices)
- Auth MAJBURIY — middleware'da JWT (2.13, 14);
socket.userni ishlating, clientga ishonmang. - To'g'ri emit target — shaxsiy xabar
io.to(user:ID), hammaga emas (2.7, maxfiylik — 14). - Rooms bilan guruhla — har suhbat/guruh/foydalanuvchi — xona 2.8-bob; user ID xonasi (ko'p qurilma).
- socket.id'ni doimiy deb ishlatmang — user ID (auth) bo'yicha kuzating 2.12-bob.
- Event ma'lumotini validatsiya qiling (Zod — 5.9, 14).
- CORS'ni cheklab qo'ying (faqat ruxsatli domen — 2.17, 14).
- Event nomlarida konvensiya (
xabar:yuborish,buyurtma:yangi— 2.6). - disconnect'da tozalang (Map/online ro'yxat — memory leak — 2.12, Misol 8).
- Masshtab uchun Redis adapter + sticky session (2.14, 2.16).
- Acknowledgement + timeout muhim xabar uchun (yetdi belgisi — 2.10).
- Rate limiting (bir client spam yubormasin — 5.20, 14);
wss://production'da. - REST + WebSocket birga — REST'dan event push 2.18-bob.
9. Amaliy loyiha: "Real-time Chat va Bildirishnoma Tizimi"
Real-time'ni to'liq, professional darajada mustahkamlash.
Maqsad
Socket.io bilan autentifikatsiyalangan, xonalarga asoslangan real-time chat va bildirishnoma tizimini qurish: guruh chat, shaxsiy xabar, typing indikatori, online holat va e-commerce bildirishnoma.
Talablar (requirements)
- Server integratsiyasi: Express (REST) + Socket.io bir portda; CORS to'g'ri (2.15, 2.17).
- JWT auth middleware: ulanishdan oldin token tekshiring;
socket.user; user ID xonasiga join (Misol 7, 2.13). - Guruh chat (rooms): xonaga kirish/chiqish; xabar faqat o'z xonasiga; tizim xabarlari (kirdi/chiqdi — Misol 3, 2.8).
- Shaxsiy xabar (lichka): qabul qiluvchining user xonasiga; o'ziga tasdiq (Misol 12, 2.8).
- Typing indikatori: "yozmoqda/to'xtadi" — suhbat xonasiga, o'zidan tashqari (Misol 12, 2.7).
- Online holat: ulangan foydalanuvchilar ro'yxati; disconnect'da tozalash (Misol 8, 2.12).
- Acknowledgement: xabar saqlanganda (timeout bilan — Misol 6, 2.10).
- E-commerce bildirishnoma: REST endpoint (buyurtma) adminlar xonasiga real-time push (Misol 10, 2.18).
- DB: xabarlarni saqlash (6); validatsiya (kelgan ma'lumot — 5.9, 14).
- Xavfsizlik: auth + validatsiya + to'g'ri emit target + CORS (2.17, 14).
- (Bonus) Redis adapter: bir nechta server uchun (Misol 11, 2.16).
- (Bonus) Namespaces:
/chatva/admin(admin'ga alohida middleware — Misol 5, 2.9).
Maslahatlar (hint)
- HTTP server:
createServer(app)new Server(httpServer)2.15-bob. - Auth:
socket.handshake.auth.tokenjwt.verifysocket.user(2.13, 5.15). - User xonasi:
socket.join(\user:${id}`)` — ko'p qurilma uchun 2.8-bob. - Shaxsiy xabar:
io.to(\user:${kimga}`).emit(...)` (maxfiylik — 14). - Typing:
socket.to(xona).emit(...)(o'zidan tashqari — 2.7). - disconnect: Map'dan o'chir (memory leak — 2.12).
- Client'da React bo'lsa: useEffect cleanup'da
socket.off/disconnect11.5-bob.
"Tayyor" mezonlari (acceptance criteria)
- Express REST + Socket.io bir portda, CORS to'g'ri.
- JWT auth — tokensiz ulanish rad etiladi.
- Guruh chat (rooms) — xabar faqat o'z xonasiga.
- Shaxsiy xabar faqat qabul qiluvchiga (hammaga emas).
- Typing indikatori ishlaydi.
- Online ro'yxat aniq (disconnect'da yangilanadi).
- Acknowledgement bilan xabar tasdiqlanadi.
- REST buyurtma admin real-time bildirishnoma.
- Xabarlar DB'da; kelgan ma'lumot validatsiyalanadi.
- (Bonus) Redis adapter / namespaces.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda zamonaviy interaktiv ilovalarning asosini — real-time aloqani — chuqur o'rgandik:
- So'rov-javob (REST) cheklovi — server o'zi push qila olmaydi; polling isrof (2.1, 2.2).
- WebSocket — doimiy, ikki tomonlama (full-duplex) kanal; HTTP handshake (
101 Switching Protocols) bilan boshlanadi (2.3, 2.4). - Socket.io — WebSocket ustida: qayta ulanish, fallback, eventlar, rooms, namespaces 2.5-bob.
- Eventlar (emit/on — 2.6); emit target (o'ziga/hammaga/broadcast/xonaga — 2.7); Rooms (guruhlash — 2.8); Namespaces (mantiqiy bo'lish — 2.9).
- Acknowledgement (tasdiqlash — 2.10); middleware/JWT auth (2.13, 14); lifecycle (connect/disconnect — 2.12).
- Transports/fallback + sticky session 2.14-bob; Redis adapter (masshtablash — 2.16); xavfsizlik (auth/CORS/validatsiya — 2.17); REST bilan birga 2.18-bob.
Keyingi bob — 5.14-bob: Telegram bot — node-telegram-bot-api va Telegraf. Real-time aloqani bildik; endi yana bir mashhur, amaliy mavzuni — Telegram bot yaratishni — o'rganamiz: bot API, buyruqlar, klaviatura, holatlar (scene), va Telegraf framework. O'zbekistonda Telegram bot — biznes uchun juda keng tarqalgan (do'kon, xizmat, bildirishnoma boti).
Foydalanilgan rasmiy/ishonchli manbalar
- socket.io/docs/v4 — Server API, Rooms, Namespaces, Broadcasting, Emit cheatsheet, Middlewares
- socket.io/docs/v4/redis-adapter — masshtablash (Pub/Sub); Using multiple nodes (sticky sessions)
- socket.io/docs/v4/middlewares — autentifikatsiya (handshake, JWT)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!