Socket.IO bilan ishlash


ULASHISH

Socket.IO nima?

Socket.IO bu kuchli JavaScript kutubxonasi bo‘lib, u real vaqt rejimida, ikki yo‘nalishli va veb-mijozlar va serverlar o‘rtasida voqealarga asoslangan aloqani ta’minlaydi. U har bir platforma, brauzer yoki qurilmada ishlashga mo‘ljallangan bo‘lib, ishonchlilik va tezlikka birdek e’tibor qaratadi.

Asosiy xususiyatlar

  • Real vaqtda ikki tomonlama aloqa - Mijozlar va serverlar o‘rtasida tezkor ma’lumotlarni uzatish imkonini beradi
  • Avtomatik qayta ulanish - O‘chirish va qayta ulanishni avtomatik tarzda boshqaradi
  • Xonani qo‘llab-quvvatlash - Guruh muloqoti uchun osongina kanallar yarating
  • Binar ma’lumotlarni qo‘llab-quvvatlash - Binar ma’lumotlarni yuborish va qabul qilish (ArrayBuffer, Blob, File va boshqalar)
  • Multiplexing - Nom maydonlari bilan bir nechta soketlarni ishlating
  • Qayta tiklash imkoniyatlari - Agar WebSockets mavjud bo‘lmasa, avtomatik ravishda HTTP uzoq so‘roviga qaytadi

Foydalanish holatlari

  • Haqiqiy vaqtda chat ilovalari
  • Jonli bildirishnomalar
  • Hamkorlik vositalari
  • Onlayn o‘yin
  • Jonli tahlil
  • Hujjat bilan hamkorlik
  • Haqiqiy vaqtda boshqaruv paneli
  • IoT ilovalari

Socket.IO ikki qismdan iborat:

  • Brauzerda ishlaydigan mijoz kutubxonasi
  • Node.js uchun server tomonidagi kutubxona

Socket.IO o‘rnatilmoqda

Server tomonida o‘rnatish

Socket.IO ni Node.js loyihangizga npm yoki ip yordamida o‘rnating:

# Using npm
npm install socket.io

# Or using Yarn
yarn add socket.io

Mijoz tomonini sozlash

Mijoz kutubxonasini kiritish uchun quyidagi usullardan birini tanlang:

Variant 1: CDN (Tez boshlash)

<script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>

Variant 2: NPM (ishlab chiqarish uchun tavsiya etiladi)

# Install the client library
npm install socket.io-client

# Or using Yarn
yarn add socket.io-client

Variant 3: ES6 modullaridan foydalanish

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

Versiyaning mosligi

Socket.IO versiyasi Node.js versiyasi Brauzerni qo‘llab-quvvatlash
v4.x v12.22.0+ Chrome 49+, Firefox 53+, Safari 10+
v3.x v10.0.0+ Chrome 49+, Firefox 53+, Safari 10+
v2.x v6.0.0+ Chrome 5+, Firefox 6+, Safari 5.1+

Eslatma: Ishlab chiqarish uchun mijoz va serverda bir xil versiyadan foydalanish tavsiya etiladi.



Socket.IO bilan oddiy suhbat ilovasi

Keling, Node.js va Socket.IO yordamida real vaqt rejimida oddiy chat ilovasini yarataylik.

Ushbu misol hech qanday loginni talab qilmaydi va asosiy funksionallikni namoyish etadi.

Serverni yaratish (app.js)

Quyidagi tarkibga ega app.js nomli yangi fayl yarating:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

// Serve static files
app.use(express.static(path.join(__dirname, 'public')));

// Simple route
app.get('/', (req, res) => {
;  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// Socket.IO connection handler
io.on('connection', (socket) => {
  console.log('A user connected');

  // Handle new messages
  socket.on('chat message', (msg) => {
    console.log('Message received:', msg);
    // Broadcast the message to all connected clients
      io.emit('chat message', msg);
    });

  // Handle disconnection
  socket.on('disconnect', () => {
    console.log('A user disconnected');
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Mijozni yaratish (public/index.html)

public katalog yarating va quyidagi tarkibga ega index.html fayl qo‘shing:

<!DOCTYPE html>
<html>
<head>
  <title>Simple Chat</title>
  <style>
  body {
    margin: 0;
    padding: 20px;
    font-family: Arial, sans-serif;
  }
  #messages {
    list-style-type: none;
    margin: 0;
    padding: 0;
    margin-bottom: 20px;
    border: 1px solid #ddd;
    padding: 10px;
    height: 400px;
    overflow-y: auto;
    }
    #messages li {
      padding: 8px 16px;
      border-bottom: 1px solid #eee;
    }
    #messages li:last-child {
      border-bottom: none;
    }
    #form {
      display: flex;
      margin-top: 10px;
    }
    #input {
      flex-grow: 1;
      padding: 10px;
      font-size: 16px;
    }
    button {
      padding: 10px 20px;
      background: #4CAF50;
      color: white;
      border: none;
      cursor: pointer;
      margin-left: 10px;
      }
    button:hover {
      background: #45a049;
    }
  </style>
</head>
<body>
  <h1>Simple Chat</h1>
  <ul id="messages"></ul>
  <form id="form" action="#">
    <input id="input" autocomplete="off" placeholder="Type your message..." />
   <button>Send</button>
   </form>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();
    const form = document.getElementById('form');
    const input = document.getElementById('input');
    const messages = document.getElementById('messages');

    // Handle form submission
    form.addEventListener('submit', (e) => {
        e.preventDefault();
        const message = input.value.trim();
        if (message) {
            // Emit the message to the server
            socket.emit('chat message', message);
                // Clear the input
                input.value = '';
            }
        });

        // Listen for incoming messages
        socket.on('chat message', (msg) => {
            const item = document.createElement('li');
            item.textContent = msg;
            messages.appendChild(item);
            // Scroll to the bottom
            messages.scrollTop = messages.scrollHeight;
        });
    </script>
</body>
</html>

Ilovani ishga tushiring

  1. Start the server:
    node app.js
  2. Brauzeringizni oching va http://localhost:3000 ga o‘ting
  3. Haqiqiy vaqtda yangilanishlarni ko‘rish uchun bir nechta brauzer oynalarini oching

Bu qanday ishlaydi

  1. Server statik fayllarga xizmat ko‘rsatish va Socket.IO ulanishini boshqarish uchun Express-dan foydalanadi
  2. Mijoz ulanganda, u barcha ulangan mijozlarga uzatiladigan xabarlarni yuborishi mumkin
  3. Mijoz tomoni JavaScript real vaqt rejimida xabarlarni yuborish va qabul qilishni boshqaradi

Keyingi qadamlar

Ushbu asosiy versiya ishlagandan so‘ng, siz quyidagilarni qo‘shishingiz mumkin:

  • Har bir xabar uchun foydalanuvchi nomlari
  • Foydalanuvchi qo‘shilish/chiqish bildirishnomalari
  • Turli xil suhbat xonalari
  • Xabarning doimiyligi
  • Foydalanuvchi autentifikatsiyasi

Eslatma: Bu namoyish qilish uchun asosiy misol. Ishlab chiqarish muhitida siz xatolarni to‘g‘ri boshqarish, kiritilgan ma’lumotlarni tekshirish va xavfsizlik choralarini qo‘shishni xohlaysiz.


Foydalanuvchi nomlarini qo‘shish

Xabarlarga foydalanuvchi nomlarini qo‘shish orqali suhbatimizni kengaytiraylik. Birinchidan, foydalanuvchi nomlarini boshqarish uchun serverni o‘zgartiring:

// In app.js, modify the connection handler
io.on('connection', (socket) => {
  console.log('A user connected');

  // Store username with socket
  socket.username = 'Anonymous';
  // Handle new messages with username
  socket.on('chat message', (msg) => {
    io.emit('chat message', {
      username: socket.username,
      message: msg,
      timestamp: new Date().toISOString()
    });
  });
  // Handle username change
  socket.on('set username', (username) => {
    const oldUsername = socket.username;
    socket.username = username || 'Anonymous';
    io.emit('user joined', {
      oldUsername: oldUsername,
      newUsername: socket.username
    });
  });

  // Handle disconnection
  socket.on('disconnect', () => {
    console.log('A user disconnected');
    io.emit('user left', { username: socket.username });
  });
});

Endi foydalanuvchi nomlarini boshqarish uchun mijozni yangilang:

<!-- Add username input at the top of the chat -->
<div id="username-container">
    <input type="text" id="username-input" placeholder="Enter your username" />
    <button id="set-username">Set Username</button>
</div>

<script>
    // Add username handling
    const usernameInput = document.getElementById('username-input');
    const setUsernameBtn = document.getElementById('set-username');
    let currentUsername = 'Anonymous';

    setUsernameBtn.addEventListener('click', () => {
        const newUsername = usernameInput.value.trim();
        if (newUsername) {
            socket.emit('set username', newUsername);
            currentUsername = newUsername;
            usernameInput.value = '';
        }
    });

    // Update message display to show usernames
    socket.on('chat message', (data) => {
        const item = document.createElement('li');
        item.innerHTML = `<strong>${data.username}:</strong> ${data.message}`;
        messages.appendChild(item);
        messages.scrollTop = messages.scrollHeight;
    });

    // Handle user join notifications
    socket.on('user joined', (data) => {
        const item = document.createElement('li');
        item.className = 'system-message';
        if (data.oldUsername === 'Anonymous') {
            item.textContent = `${data.newUsername} has joined the chat`;
        } else {
            item.textContent = `${data.oldUsername} is now known as ${data.newUsername}`;
        }
        messages.appendChild(item);
        messages.scrollTop = messages.scrollHeight;
    });

    // Handle user leave notifications
    socket.on('user left', (data) => {
        const item = document.createElement('li');
        item.className = 'system-message';
        item.textContent = `${data.username} has left the chat`;
        messages.appendChild(item);
        messages.scrollTop = messages.scrollHeight;
    });
</script>

<style>
.system-message {
    color: #666;
    font-style: italic;
    font-size: 0.9em;
}
</style>

Chat xonalarini qo‘shish

Keling, turli chat xonalarini yaratish va ularga qo‘shilish imkoniyatini qo‘shaylik. Birinchidan, serverni yangilang:

// In app.js, add room handling
const rooms = new Set(['general', 'random']);

io.on('connection', (socket) => {
  // ... existing code ...

  // Join a room
  socket.on('join room', (room) => {
    // Leave all rooms except the default one
    socket.rooms.forEach(r => {
      if (r !== socket.id) {
        socket.leave(r);
        socket.emit('left room', r);
      }
    });

    // Join the new room
    socket.join(room);
    socket.emit('joined room', room);

    // Notify others in the room
    socket.to(room).emit('room message', {
      username: 'System',
      message: `${socket.username} has joined the room`,
      timestamp: new Date().toISOString()
    });
  });

  // Handle room creation
  socket.on('create room', (roomName) => {
    if (!rooms.has(roomName)) {
      rooms.add(roomName);
      io.emit('room created', roomName);
    }
  });

  // Modify message handler to send to room
  socket.on('chat message', (data) => {
    const room = Array.from(socket.rooms).find(r => r !== socket.id) || 'general';
    io.to(room).emit('chat message', {
      username: socket.username,
      message: data.message,
      timestamp: new Date().toISOString(),
      room: room
    });
  });
});

Xonalarni boshqarish uchun mijozni yangilang:

<div id="chat-container">
    <div id="sidebar">
        <h3>Rooms</h3>
        <ul id="room-list">
            <li class="room active" data-room="general">General</li>
            <li class="room" data-room="random">Random</li>
        </ul>
        <div id="create-room">
            <input type="text" id="new-room" placeholder="New room name" />
            <button id="create-room-btn">Create Room</button>
        </div>
    </div>
    <div id="chat-area">
        <div id="messages"></div>
        <form id="form">
            <input id="input" autocomplete="off" />
            <button>Send</button>
        </form>
    </div>
</div>

<script>
    // Room handling
    const roomList = document.getElementById('room-list');
    const newRoomInput = document.getElementById('new-room');
    const createRoomBtn = document.getElementById('create-room-btn');
    let currentRoom = 'general';

    // Join room when clicking on room in the list
    roomList.addEventListener('click', (e) => {
        if (e.target.classList.contains('room')) {
            const room = e.target.dataset.room;
            socket.emit('join room', room);
            currentRoom = room;
            document.querySelectorAll('.room').forEach(r => r.classList.remove('active'));
            e.target.classList.add('active');
        }
    });

    // Create new room
    createRoomBtn.addEventListener('click', () => {
        const roomName = newRoomInput.value.trim();
        if (roomName && !document.querySelector(`[data-room="${roomName}"]`)) {
            socket.emit('create room', roomName);
            newRoomInput.value = '';
        }
    });

    // Handle new room creation
    socket.on('room created', (roomName) => {
        const roomItem = document.createElement('li');
        roomItem.className = 'room';
        roomItem.dataset.room = roomName;
        roomItem.textContent = roomName;
        roomList.appendChild(roomItem);
    });

    // Handle room join confirmation
    socket.on('joined room', (room) => {
        const item = document.createElement('li');
        item.className = 'system-message';
        item.textContent = `You joined ${room}`;
        messages.appendChild(item);
        currentRoom = room;
        messages.scrollTop = messages.scrollHeight;
    });

    // Handle room messages
    socket.on('room message', (data) => {
        const item = document.createElement('li');
        item.className = 'system-message';
        item.textContent = data.message;
        messages.appendChild(item);
        messages.scrollTop = messages.scrollHeight;
    });
</script>

<style>
#chat-container {
    display: flex;
    max-width: 1200px;
    margin: 0 auto;
}

#sidebar {
    width: 250px;
    padding: 20px;
    background-color: #f5f5f5;
    border-right: 1px solid #ddd;
}

#chat-area {
    flex: 1;
    padding: 20px;
}

.room {
    padding: 8px;
    cursor: pointer;
    border-radius: 4px;
    margin: 4px 0;
}

.room:hover {
    background-color: #e9e9e9;
}

.room.active {
    background-color: #4CAF50;
    color: white;
}

#create-room {
    margin-top: 20px;
}

#new-room {
    width: 100%;
    padding: 8px;
    margin-bottom: 8px;
}

#create-room-btn {
    width: 100%;
    padding: 8px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

#create-room-btn:hover {
    background-color: #45a049;
}
</style>

Foydalanuvchilar ro‘yxatini qo‘shish va yozish ko‘rsatkichlari

Keling, foydalanuvchilar ro‘yxati va indikatorlarni yozish bilan suhbatimizni yaxshilaylik. Birinchidan, foydalanuvchilarni va yozish holatini kuzatish uchun serverni yangilang:

// In app.js, track users and typing status
const usersInRooms = new Map();
const typingUsers = new Map();

io.on('connection', (socket) => {
   // ... existing code ...

   // Initialize user data
   socket.on('join room', (room) => {
     // ... existing join room code ...

     // Initialize user data for the room
     if (!usersInRooms.has(room)) {
         usersInRooms.set(room, new Map());
         typingUsers.set(room, new Set());
     }

     // Add user to room
     usersInRooms.get(room).set(socket.id, {
         username: socket.username,
         id: socket.id
     });
    
     // Send updated user list to room
     updateUserList(room);
   });

   // Handle typing status
   socket.on('typing', (isTyping) => {
     const room = Array.from(socket.rooms).find(r => r !== socket.id);
     if (!room) return;
    
     if (isTyping) {
         typingUsers.get(room).add(socket.username);
     } else {
         typingUsers.get(room).delete(socket.username);
     }
    
     // Notify room about typing users
     io.to(room).emit('typing users', Array.from(typingUsers.get(room)));
   });

   // Handle disconnection
   socket.on('disconnect', () => {
     // Remove from all rooms
     Array.from(usersInRooms.entries()).forEach(([room, users]) => {
         if (users.has(socket.id)) {
            users.delete(socket.id);
            typingUsers.get(room)?.delete(socket.username);
            updateUserList(room);
         }
     }
   });
   });

   // Helper function to update user list for a room
   function updateUserList(room) {
     const users = Array.from(usersInRooms.get(room)?.values() || []);
     io.to(room).emit('user list', {
         room: room,
         users: users.map(u => ({
            username: u.username,
            isTyping: typingUsers.get(room)?.has(u.username) || false
         }))
     });
   }
   });
});

Foydalanuvchilar ro‘yxatini ko‘rsatish va yozish ko‘rsatkichlarini boshqarish uchun mijozni yangilang:

<div id="chat-container">
  <div id="sidebar">
    <h3>Rooms</h3>
    <ul id="room-list">
     <!-- Room list will be populated here -->
    </ul>
    <div id="create-room">
     <input type="text" id="new-room" placeholder="New room name" />
     <button id="create-room-btn">Create Room</button>
    </div>
    <h3>Users in Room</h3>
    <ul id="user-list">
     <!-- User list will be populated here -->
    </ul>
  </div>
  <div id="chat-area">
    <div id="typing-indicator"></div>
    <div id="messages"></div>
    <form id="form">
     <input id="input" autocomplete="off" placeholder="Type a message..." />
     <button>Send</button>
    </form>
  </div>
</div>

<script>
   // ... existing code ...

   const userList = document.getElementById('user-list');
   const typingIndicator = document.getElementById('typing-indicator');
   const messageInput = document.getElementById('input');
   let typingTimeout;

   // Handle typing events
   messageInput.addEventListener('input', () => {
     // User is typing
     if (!typingTimeout) {
         socket.emit('typing', true);
     }
    
     // Clear previous timeout
     clearTimeout(typingTimeout);
    
     // Set a timeout to indicate user stopped typing
     typingTimeout = setTimeout(() => {
         socket.emit('typing', false);
         typingTimeout = null;
     }, 1000);
   });

   // Handle form submission
   form.addEventListener('submit', (e) => {
     e.preventDefault();
     if (messageInput.value.trim()) {
         socket.emit('chat message', {
            message: messageInput.value,
            room: currentRoom
         });
         messageInput.value = '';
        
         // Clear typing status
         if (typingTimeout) {
            clearTimeout(typingTimeout);
            typingTimeout = null;
            socket.emit('typing', false);
         }
     }
   });

   // Update user list
   socket.on('user list', (data) => {
     if (data.room === currentRoom) {
         userList.innerHTML = '';
         data.users.forEach(user => {
            const userItem = document.createElement('li');
            userItem.textContent = user.username;
            if (user.isTyping) {
               userItem.innerHTML += ' <span class="typing">typing...</span>';
            }
            userList.appendChild(userItem);
         });
     }
   });

   // Update typing indicator
   socket.on('typing users', (usernames) => {
     const typingUsers = usernames.filter(u => u !== currentUsername);
     if (typingUsers.length > 0) {
         typingIndicator.textContent = `${typingUsers.join(', ')} ${typingUsers.length > 1 ? 'are' : 'is'} typing...`;
         typingIndicator.style.display = 'block';
     } else {
         typingIndicator.style.display = 'none';
     }
   });
</script>

<style>
   /* Add to existing styles */
   #typing-indicator {
         color: #666;
         font-style: italic;
         font-size: 0.9em;
         padding: 5px 10px;
         display: none;
   }

   .typing {
         color: #666;
         font-size: 0.8em;
         font-style: italic;
   }

   #user-list {
         list-style: none;
         padding: 0;
         margin: 10px 0;
   }

   #user-list li {
         padding: 5px 10px;
         border-radius: 3px;
         margin: 2px 0;
   }

   #user-list li:hover {
         background-color: #f0f0f0;
   }
</style>

Client Side API umumiy ko‘rinishi

Mijoz tomonidagi Socket.IO API quyidagi usullarni taqdim etadi:

  • io() - Serverga ulanadi
  • socket.emit() - hodisani serverga yuboradi
  • socket.on() - Serverdagi voqealarni tinglaydi
  • socket.disconnect() - serverdan uziladi

Socket.IO voqealari

Socket.IO aloqa uchun voqealarga asoslangan arxitekturadan foydalanadi. Mana bir nechta asosiy voqealar:

O‘rnatilgan tadbirlar

  • connect - Ulanish vaqtida ishga tushirildi
  • disconnect - Aloqa uzilgandan keyin ishga tushirildi
  • error - xato tufayli ishga tushirildi
  • reconnect - Muvaffaqiyatli qayta ulanishdan keyin ishga tushirildi
  • reconnect_attempt - qayta ulanishga urinishda ishga tushirildi

Socket.IO o‘rta dasturi

Socket.IO autentifikatsiya va boshqa maqsadlar uchun o‘rta dastur funksiyalarini aniqlash imkonini beradi:

const io = new Server(server);

// Middleware for authentication
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  
  if (!token) {
    return next(new Error('Authentication error: Token missing'));
  }
  
  // Verify token (example with JWT)
  try {
    const user = jwt.verify(token, 'your-secret-key');
    socket.user = user;
    next();
  } catch (error) {
    next(new Error('Authentication error: Invalid token'));
  }
});

io.on('connection', (socket) => {
  console.log(`Authenticated user connected: ${socket.user.username}`);
});

Socket.IO va Native WebSockets

                                                                                                                                                                       
XususiyatSocket.IOMahalliy WebSockets
Qayta tiklash mexanizmlariYes (HTTP long-polling, etc.)Yo‘q
Avtomatik qayta ulanishHaNo (must implement)
EshittirishO‘rnatilganQo‘lda amalga oshirish
Xonalar/Ismlar maydoniO‘rnatilganQo‘lda amalga oshirish
Brauzerni qo‘llab-quvvatlashBarcha brauzerlarFaqat zamonaviy brauzerlar
Paket hajmiLarger (protocol overhead)Kichikroq
Ikkilik ma’lumotlarQo‘llab-quvvatlanadiQo‘llab-quvvatlanadi

Ishonchlilik, muvofiqlik va yuqori darajadagi funksiyalar kerak bo‘lganda Socket.IO afzal ko‘riladi, mahalliy WebSockets esa engilroq va kamroq yukga ega.



W3Schools Pathfinder

Yutuqlaringizni kuzating – bu bepul!