WebSockets — real-time ilovalar

~30 daq · NestJS · Ilg'or mavzular

Muammodan boshlaylik

Chat ilovasi yozyapsiz. Foydalanuvchi A xabar yubordi — foydalanuvchi B darhol ko'rishi kerak.

Oddiy HTTP bilan bu ishlamaydi:

Foydalanuvchi B har 2 soniyada so'rashi kerak:
"Yangi xabar bormi?" → "Yo'q"
"Yangi xabar bormi?" → "Yo'q"
"Yangi xabar bormi?" → "Ha, mana"

Bu polling — server qayta-qayta so'raladi, resurs behuda sarflanadi. Xabar kelishi bilan emas, keyingi so'rovda ko'rinadi.

Real-time kerak: xabar kelsa — server o'zi mijozga yubora olsin.


WebSocket nima?

WebSocket — server va client orasida ikki tomonlama doimiy ulanish.

HTTP da har so'rovdan keyin ulanish yopiladi. WebSocket da — bir marta ulanasiz va ikkalasi ham istaganida xabar yuborishi mumkin.

HTTP:
  Client ──► Server (so'rov)
  Client ◄── Server (javob)
  (ulanish yopildi)

WebSocket:
  Client ◄──────────────────► Server
    xabar yubor →
                ← xabar ol
    yana yubor →

Telegram, WhatsApp, online o'yinlar — barchasi WebSocket ishlatadi.


Hayotiy o'xshatma

Tasavvur qiling: radio aloqasi. Politsiya xodimlari radiopriyomnik orqali bir-biri bilan gaplashadi. Biri gapirsa — hammasi eshitadi. Ikki tomonlama, doimiy ulanish. HTTP esa aksincha — har safar telefondan qo'ng'iroq qilib, javob kutib, ulanish yopiladi.


Nima uchun Gateway?

NestJS da WebSocket server Gateway deb ataladi — Controller ga o'xshaydi, lekin HTTP emas, socket eventlarini tinglaydi.

HTTP:   @Controller → @Get/@Post → HTTP so'rov
Socket: @WebSocketGateway → @SubscribeMessage → socket event

O'rnatish

npm install @nestjs/websockets @nestjs/platform-socket.io socket.io

Fayl tuzilmasi

src/
  chat/
    chat.gateway.ts    ← WebSocket server
    chat.module.ts

Gateway yaratish

// chat/chat.gateway.ts
import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  MessageBody,
} from '@nestjs/websockets';
import { Server } from 'socket.io';

@WebSocketGateway({ cors: { origin: '*' } })
export class ChatGateway {

  @WebSocketServer()
  server: Server; // ← barcha ulanganlar bilan ishlash uchun

  // Client 'sendMessage' event yuborganda bu ishga tushadi
  @SubscribeMessage('sendMessage')
  handleMessage(@MessageBody() text: string) {
    // Barcha ulanganlarga qaytarib yuborish
    this.server.emit('newMessage', { text, time: new Date() });
  }
}

Module ga qo'shish

// chat/chat.module.ts
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';

@Module({
  providers: [ChatGateway],
})
export class ChatModule {}

AppModule ga ChatModule ni import qiling.


Xonalar (Rooms)

Chat da odatda xonalar bo'ladi — har bir xona alohida guruh. "Umumiy" xonadagilar faqat o'z xonalarini ko'radi.

import { ConnectedSocket, MessageBody, SubscribeMessage } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@WebSocketGateway({ cors: { origin: '*' } })
export class ChatGateway {

  @WebSocketServer()
  server: Server;

  // Xonaga qo'shilish
  @SubscribeMessage('joinRoom')
  joinRoom(@MessageBody() room: string, @ConnectedSocket() client: Socket) {
    client.join(room); // ← client shu xonaga qo'shildi
  }

  // Faqat shu xona a'zolariga yuborish
  @SubscribeMessage('sendMessage')
  sendToRoom(
    @MessageBody() data: { room: string; text: string },
    @ConnectedSocket() client: Socket,
  ) {
    this.server.to(data.room).emit('newMessage', {
      text: data.text,
      from: client.id,
    });
  }
}

Ulanish va uzilishni kuzatish

import { OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';

@WebSocketGateway({ cors: { origin: '*' } })
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {

  handleConnection(client: Socket) {
    console.log(`Ulandi: ${client.id}`);
  }

  handleDisconnect(client: Socket) {
    console.log(`Uzildi: ${client.id}`);
  }
}

Frontend dan ulash

import { io } from 'socket.io-client';

const socket = io('http://localhost:3000');

// Xonaga kirish
socket.emit('joinRoom', 'general');

// Xabar yuborish
socket.emit('sendMessage', { room: 'general', text: 'Salom!' });

// Yangi xabarni qabul qilish
socket.on('newMessage', (data) => {
  console.log(`${data.from}: ${data.text}`);
});

Tez-tez uchraydigan xatolar

1. CORS xatosi

Cross-Origin Request Blocked

Sababi: Gateway CORS sozlanmagan. Yechim: @WebSocketGateway({ cors: { origin: 'http://yourfrontend.com' } })


2. Xabar kelmayapti Sababi: socket.emit('sendMessage') va @SubscribeMessage('sendMessage') — nomlar mos kelmayapti. Yechim: Ikki tomonda event nomi aynan bir xil bo'lsin, katta-kichik harf muhim.


3. Nginx da WebSocket ishlamayapti Sababi: Nginx Upgrade headerni o'tkazmayapti. Yechim: Nginx konfiguratsiyaga qo'shing:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Xulosa

  • WebSocket — ikki tomonlama doimiy ulanish, HTTP emas
  • @WebSocketGateway() — WebSocket server yaratadi
  • @SubscribeMessage('event') — shu nomdagi eventni tinglaydi
  • server.emit() — barcha ulanganlarga yuboradi
  • server.to(room).emit() — faqat xona a'zolariga yuboradi

Keyingi dars: 38-dars: Server-Sent Events — bir tomonlama real-time