WebSocket — ws moduli bilan, handshake, event lar
Muammodan boshlaylik
Chat ilovasi yasayapsiz. Foydalanuvchi xabar yuborganda — boshqalar darhol ko'rishi kerak.
HTTP bilan bu qanday ishlaydi?
Ali: "Salom" yubordi
Vali: Yangi xabar bormi? (so'rov) → "yo'q"
Vali: Yangi xabar bormi? (so'rov) → "yo'q"
Vali: Yangi xabar bormi? (so'rov) → "ha, Ali salom dedi"
Vali har soniyada server dan so'rab turadi. 1000 foydalanuvchi bo'lsa — sekundiga 1000 ta so'rov. Ko'pchiligi "yo'q" javob qaytaradi. Server befoyda ishlarni qiladi.
Asosiy muammo: HTTP — bir tomonlama. Siz so'raysiz, server javob beradi. Server o'zi xabar yubora olmaydi.
WebSocket bu muammoni tubdan hal qiladi.
WebSocket nima?
WebSocket — server va mijoz o'rtasida doimiy, ikki tomonlama ulanish.
Hayotiy o'xshatma
HTTP va WebSocket ni telefon qo'ng'irog'i bilan tushuntiraylik:
HTTP — SMS. Har xabar uchun alohida "qo'ng'iroq":
Ali → Server: "Salom" (ulanish → so'rov → javob → ulanish yopiladi)
Ali → Server: "Qalaysiz?" (yana ulanish → so'rov → javob → yopiladi)
WebSocket — telefon qo'ng'irog'i. Bir marta bog'lanasiz, keyin ikkalangiz xohlagan vaqtda gaplashasiz:
Ali ←→ Server: ulanish o'rnatildi
Ali → Server: "Salom"
Server → Ali: "Yaxshi"
Server → Ali: "Vali ham kirdi" ← server o'zi xabar yubordi!
Ali → Server: "Privet Vali"
HTTP vs WebSocket — texnik farq
HTTP:
Mijoz ──(so'rov)──► Server
Mijoz ◄──(javob)── Server
[ulanish yopiladi]
WebSocket:
Mijoz ◄──────────────────► Server
[doimiy ochiq kanal]
ikkalasi ham istalgan
vaqtda xabar yuboradi
Handshake — qanday boshlanadi?
WebSocket HTTP dan boshlanadi — bu ajablanarli. Lekin mantiqli sababi bor: HTTP ni barcha brauzerlar, serverlar, proxylar tushunadi. WebSocket shu yo'l orqali "upgrade" so'raydi.
1. Mijoz HTTP so'rov yuboradi:
GET /chat HTTP/1.1
Upgrade: websocket ← "HTTP dan WebSocket ga o'tmoqchiman"
Connection: Upgrade
Sec-WebSocket-Key: abc123 ← tasodifiy kalit
2. Server rozi bo'lsa:
HTTP/1.1 101 Switching Protocols ← "kelishdik"
Upgrade: websocket
Sec-WebSocket-Accept: xyz789 ← kalitdan hosil qilingan javob
3. Endi HTTP emas — WebSocket ulanishi boshlanadi
101 Switching Protocols — bu maxsus HTTP status kodi. "Men HTTP dan boshqa protokolga o'tyapman" degani. Shundan keyin HTTP tugadi, WebSocket boshlandi.
ws moduli — eng oddiy misol
npm install ws
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Yangi ulanish kelganda
wss.on('connection', (socket) => {
console.log('Yangi mijoz ulandi');
// Mijozdan xabar kelganda
socket.on('message', (data) => {
console.log('Keldi:', data.toString());
// Javob qaytarish
socket.send('Xabaringizni oldim!');
});
// Ulanish uzilganda
socket.on('close', () => {
console.log('Mijoz chiqib ketdi');
});
});
// client.js (Node.js yoki brauzer)
const ws = new WebSocket('ws://localhost:8080');
ws.on('open', () => {
console.log('Ulandi!');
ws.send('Salom server!');
});
ws.on('message', (data) => {
console.log('Serverdan:', data.toString());
});
ws.on('close', () => {
console.log('Ulanish uzildi');
});
Express bilan birga ishlatish
Ko'pincha WebSocket va HTTP server bir portda ishlashi kerak:
// app.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app); // ← Express ni HTTP serverga o'rash
const wss = new WebSocket.Server({ server }); // ← Xuddi shu serverda WS
// HTTP route (oddiy)
app.get('/', (req, res) => {
res.send('Salom!');
});
// WebSocket ulanishlari
wss.on('connection', (socket) => {
socket.send('Xush kelibsiz!');
socket.on('message', (data) => {
console.log('Xabar:', data.toString());
});
});
// Bitta server — ikkala protokol
server.listen(3000, () => {
console.log('Server 3000 portda');
// HTTP: http://localhost:3000
// WebSocket: ws://localhost:3000
});
Real misol: oddiy chat
Har bir kelgan xabarni barcha ulangan mijozlarga yuborish — broadcast:
// server.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
// Yordamchi: barcha ulanishli mijozlarga yuborish
const broadcast = (message, senderSocket = null) => {
wss.clients.forEach((client) => {
// Faqat ulanish ochiq bo'lsa va yuborgandan boshqasiga
if (client.readyState === WebSocket.OPEN && client !== senderSocket) {
client.send(message);
}
});
};
wss.on('connection', (socket) => {
console.log('Ulandi. Jami:', wss.clients.size);
// Yangi odam kirdi — hammaga xabar
broadcast(JSON.stringify({ type: 'info', text: 'Yangi foydalanuvchi kirdi' }));
socket.on('message', (data) => {
const message = data.toString();
// Barcha boshqalarga yuborish
broadcast(JSON.stringify({ type: 'message', text: message }), socket);
});
socket.on('close', () => {
console.log('Chiqdi. Qoldi:', wss.clients.size);
broadcast(JSON.stringify({ type: 'info', text: 'Foydalanuvchi chiqdi' }));
});
});
server.listen(3000);
Brauzerda:
// Frontend (brauzer)
const ws = new WebSocket('ws://localhost:3000');
ws.onopen = () => {
console.log('Ulandi!');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'message') {
console.log('Xabar:', data.text);
} else if (data.type === 'info') {
console.log('Ma\'lumot:', data.text);
}
};
// Xabar yuborish
document.getElementById('send').onclick = () => {
const text = document.getElementById('input').value;
ws.send(text);
};
socket.readyState — ulanish holati
// Xabar yuborishdan oldin ulanish ochiqligini tekshiring
if (socket.readyState === WebSocket.OPEN) {
socket.send('Xabar');
}
// To'rtta holat:
// WebSocket.CONNECTING (0) — ulanilmoqda
// WebSocket.OPEN (1) — ochiq, yuborish mumkin
// WebSocket.CLOSING (2) — yopilmoqda
// WebSocket.CLOSED (3) — yopilgan
Arxitektura — katta loyiha uchun
project/
├── server.js ← HTTP + WS server
├── websocket/
│ ├── index.js ← wss sozlamasi, connection handler
│ └── handlers/
│ ├── chatHandler.js ← chat xabarlari
│ └── infoHandler.js ← tizim xabarlari
└── app.js ← Express routelar
// websocket/index.js
const WebSocket = require('ws');
const chatHandler = require('./handlers/chatHandler');
const initWebSocket = (server) => {
const wss = new WebSocket.Server({ server });
wss.on('connection', (socket) => {
chatHandler(socket, wss); // ← logika alohida faylda
});
return wss;
};
module.exports = initWebSocket;
// websocket/handlers/chatHandler.js
const WebSocket = require('ws');
module.exports = (socket, wss) => {
socket.on('message', (data) => {
let parsed;
try {
parsed = JSON.parse(data);
} catch {
socket.send(JSON.stringify({ error: 'Noto\'g\'ri format' }));
return;
}
if (parsed.type === 'chat') {
// Barcha ochiq ulanishlarga yuborish
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'chat',
text: parsed.text,
time: new Date().toISOString(),
}));
}
});
}
});
socket.on('close', () => {
console.log('Foydalanuvchi chiqdi');
});
};
// server.js
const http = require('http');
const app = require('./app');
const initWebSocket = require('./websocket');
const server = http.createServer(app);
initWebSocket(server); // ← WebSocket ulanadi
server.listen(3000);
Tez-tez uchraydigan xatolar
1. readyState tekshirmasdan yuborish
// ❌ — ulanish yopiq bo'lsa xato chiqadi
socket.send('Xabar');
// ✅
if (socket.readyState === WebSocket.OPEN) {
socket.send('Xabar');
}
Sababi: ulanish to'satdan uzilishi mumkin. Yopiq socketga send() — Error: WebSocket is not open xatosi.
2. Xomdan matn yuborish — JSON ishlatmay
// ❌ — tur aniqlanmagan, kengaytirilmaydi
socket.send('salom');
socket.send('yangi_foydalanuvchi:Ali'); // ← o'z formatini ixtiro qilish
// ✅ — JSON: har xabar turga ega, kengaytiriladi
socket.send(JSON.stringify({ type: 'chat', text: 'salom', from: 'Ali' }));
socket.send(JSON.stringify({ type: 'join', username: 'Ali' }));
socket.send(JSON.stringify({ type: 'leave', username: 'Ali' }));
Sababi: JSON bilan turga qarab ishlash mumkin. Matn bilan murakkablashganda parse qilish qiyin.
3. try/catch siz JSON.parse
// ❌ — noto'g'ri JSON kelsa server crash
socket.on('message', (data) => {
const parsed = JSON.parse(data); // ← xato chiqsa to'xtaydi
});
// ✅
socket.on('message', (data) => {
try {
const parsed = JSON.parse(data);
// ...
} catch {
socket.send(JSON.stringify({ error: 'JSON format noto\'g\'ri' }));
}
});
4. Xabar yuborganning o'ziga qaytarish
// ❌ — chat da o'z xabaringiz ikki marta ko'rinadi
wss.clients.forEach((client) => {
client.send(message); // ← yuborganga ham qaytadi
});
// ✅ — yuborgandan boshqasiga
wss.clients.forEach((client) => {
if (client !== socket && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
Xulosa
- WebSocket — doimiy, ikki tomonlama kanal: server ham, mijoz ham istagan vaqtda xabar yuboradi
- Handshake — HTTP orqali boshlanadi (
101 Switching Protocols), keyin WebSocket wss.clients— barcha ulangan mijozilar,forEachbilan broadcast- Har doim
readyState === OPENtekshiring - Xabarlarni JSON formatida yuboring — tur (
type) bilan - Katta loyihada: handler larni alohida fayllarga ajrating
Keyingi dars: 43-dars: Socket.io — o'rnatish, room, namespace, broadcast