WebSocket

ВСория

Π’Π΅Π±-сокСты - это ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» для Π΄Π²ΡƒΠ½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎΠΉ связи ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ ΠΈ сСрвСром Ρ‡Π΅Ρ€Π΅Π· ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚. Они ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ постоянноС соСдинСниС ΠΌΠ΅ΠΆΠ΄Ρƒ Π±Ρ€Π°ΡƒΠ·Π΅Ρ€ΠΎΠΌ ΠΈ сСрвСром, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Π΅ Π² Π²Π΅Π±-сокСтах, Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‚:

  1. WebSocket(): конструктор ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° WebSocket, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ создаСт Π½ΠΎΠ²Ρ‹ΠΉ экзСмпляр Π²Π΅Π±-сокСта.
  2. send(): ΠΌΠ΅Ρ‚ΠΎΠ΄, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ отправляСт Π΄Π°Π½Π½Ρ‹Π΅ Π½Π° сСрвСр Ρ‡Π΅Ρ€Π΅Π· Π²Π΅Π±-сокСт.
  3. close(): ΠΌΠ΅Ρ‚ΠΎΠ΄, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π·Π°ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ Π²Π΅Π±-сокСт.
  4. onopen(): событиС, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ происходит ΠΏΡ€ΠΈ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ соСдинСния с сСрвСром.
  5. onmessage(): событиС, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ происходит ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ сообщСния ΠΎΡ‚ сСрвСра.
  6. onerror(): событиС, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ происходит ΠΏΡ€ΠΈ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΈ ошибки Π²ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π²Π΅Π±-сокСтами.

ΠŸΡ€ΠΈ использовании Π²Π΅Π±-сокСтов, ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΠΈ сСрвСр ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°ΡŽΡ‚ постоянноС соСдинСниС. Как Ρ‚ΠΎΠ»ΡŒΠΊΠΎ соСдинСниС установлСно, Π΄Π°Π½Π½Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒ Π² ΠΎΠ±ΠΎΠΈΡ… направлСниях Π±Π΅Π· нСобходимости ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΈΠ»ΠΈ ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ страницы.

ΠŸΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π΅ Π΄Π°Π½Π½Ρ‹Ρ… Ρ‡Π΅Ρ€Π΅Π· Π²Π΅Π±-сокСты, Π΄Π°Π½Π½Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ Π·Π°ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹ Π² Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π°Ρ…, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ JSON, XML ΠΈΠ»ΠΈ Π±ΠΈΠ½Π°Ρ€Π½Ρ‹ΠΉ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚. Π’Π΅Π±-сокСты Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ‹, Π°ΡƒΠ΄ΠΈΠΎ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠΈ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ Ρ‚ΠΈΠΏΡ‹ Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.

РСализация

На сторонС сСрвСра создаСтся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ rooms, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ содСрТит список всСх ΠΊΠΎΠΌΠ½Π°Ρ‚ ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅. ΠŸΡ€ΠΈ создании Π½ΠΎΠ²ΠΎΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ сСрвСр провСряСт, сущСствуСт Π»ΠΈ ΡƒΠΆΠ΅ ΠΊΠΎΠΌΠ½Π°Ρ‚Π° с Ρ‚Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΈΠΌΠ΅Π½Π΅ΠΌ, ΠΈ отправляСт сообщСниС ΠΎΠ± ошибкС, Ссли ΠΊΠΎΠΌΠ½Π°Ρ‚Π° ΡƒΠΆΠ΅ сущСствуСт. Если ΠΊΠΎΠΌΠ½Π°Ρ‚Π° Π½Π΅ сущСствуСт, сСрвСр создаСт Π½ΠΎΠ²ΡƒΡŽ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ ΠΈ добавляСт создатСля ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ Π² список Π΅Π΅ участников.

Π’ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ΅ для присоСдинСния ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅ сСрвСр провСряСт, сущСствуСт Π»ΠΈ ΠΊΠΎΠΌΠ½Π°Ρ‚Π° с ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π΅ΠΌ, ΠΈ отправляСт сообщСниС ΠΎΠ± ошибкС, Ссли ΠΊΠΎΠΌΠ½Π°Ρ‚Π° Π½Π΅ сущСствуСт. Если ΠΊΠΎΠΌΠ½Π°Ρ‚Π° сущСствуСт, сСрвСр добавляСт ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² список Π΅Π΅ участников ΠΈ отправляСт всСм участникам ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ сообщСниС ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ присоСдинился ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅.

ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ сообщСния Π² ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ просто ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ всСм участникам ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΌΠ΅Ρ‚ΠΎΠ΄ io.to(roomName).emit().

ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΎΡ‚ Ρ‡Π°Ρ‚Π° удаляСт ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ· всСх ΠΊΠΎΠΌΠ½Π°Ρ‚ ΠΈ отправляСт сообщСниС ΠΎΠ± ΡƒΡ…ΠΎΠ΄Π΅ всСм участникам ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… ΠΊΠΎΠΌΠ½Π°Ρ‚.

server.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
 
// список ΠΊΠΎΠΌΠ½Π°Ρ‚ ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅
const rooms = {};
 
app.use(express.static(__dirname + '/public'));
 
io.on('connection', socket => {
  console.log('User connected');
 
  // ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для создания ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
  socket.on('create_room', roomName => {
    console.log('Creating room', roomName);
    if (!rooms[roomName]) {
      // Ссли ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ Π½Π΅ сущСствуСт - создаСм Π½ΠΎΠ²ΡƒΡŽ
      rooms[roomName] = [socket.id];
      socket.join(roomName);
      socket.emit('created_room', { roomName, participants: [] });
    } else {
      // ΠΈΠ½Π°Ρ‡Π΅ отправляСм сообщСниС ΠΎΠ± ошибкС
      socket.emit('error', 'Room already exists');
    }
  });
 
  // ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для присоСдинСния ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅
  socket.on('join_room', ({ roomName, username }) => {
    console.log(`${username} is joining ${roomName}`);
    if (rooms[roomName]) {
      // Ссли ΠΊΠΎΠΌΠ½Π°Ρ‚Π° сущСствуСт - добавляСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² Π½Π΅Π΅
      rooms[roomName].push(socket.id);
      socket.join(roomName);
      const participants = rooms[roomName].map(id =>
        io.sockets.connected[id].username
      );
      io.to(roomName).emit('joined_room', { roomName, participants });
      socket.emit('room_joined', { roomName, username });
    } else {
      // ΠΈΠ½Π°Ρ‡Π΅ отправляСм сообщСниС ΠΎΠ± ошибкС
      socket.emit('error', 'Room does not exist');
    }
  });
 
  // ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ сообщСния Π² ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ
  socket.on('send_message', ({ roomName, message }) => {
    console.log(`${socket.username} sent a message to ${roomName}: ${message}`);
    io.to(roomName).emit('new_message', { roomName, message, sender: socket.username });
  });
 
  // ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΎΡ‚ Ρ‡Π°Ρ‚Π°
  socket.on('disconnect', () => {
    console.log(`User disconnected: ${socket.username}`);
    // удаляСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ· всСх ΠΊΠΎΠΌΠ½Π°Ρ‚, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΎΠ½ находился
    Object.keys(rooms).forEach(roomName => {
      if (rooms[roomName].includes(socket.id)) {
        rooms[roomName] = rooms[roomName].filter(id => id !== socket.id);
        const participants = rooms[roomName].map(id =>
          io.sockets.connected[id].username
        );
        io.to(roomName).emit('user_left', { roomName, participants, username: socket.username });
      }
    });
  });
 
  // добавляСм имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΊ сокСту
  socket.on('set_username', username => {
    socket.username = username;
    console.log(`Username set: ${socket.username}`);
  });
});
 
server.listen(3000, () => {
  console.log('Server listening on port 3000');
});

ΠšΠ»ΠΈΠ΅Π½Ρ‚ΡΠΊΠΈΠΉ ΠΊΠΎΠ΄ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Π΄Π²Π΅ основныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ - joinRoom() для присоСдинСния ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅ ΠΈ switchRoom() для ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π½Π° Π½ΠΎΠ²ΡƒΡŽ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ. Когда ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ присоСдиняСтся ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅, ΠΎΠ½ отправляСт Π½Π° сСрвСр сообщСниС join_room с ΠΈΠΌΠ΅Π½Π΅ΠΌ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ ΠΈ своим ΠΈΠΌΠ΅Π½Π΅ΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ. Π‘Π΅Ρ€Π²Π΅Ρ€ добавляСт ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² список участников ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ ΠΈ отправляСт всСм участникам ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ сообщСниС joined_room с списком всСх участников. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π·, ΠΊΠΎΠ³Π΄Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ отправляСт сообщСниС Π² ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ, клиСнтский ΠΊΠΎΠ΄ отправляСт Π½Π° сСрвСр сообщСниС send_message, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ содСрТит тСкст сообщСния ΠΈ имя Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹. Π‘Π΅Ρ€Π²Π΅Ρ€ Π·Π°Ρ‚Π΅ΠΌ отправляСт всСм участникам ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹ сообщСниС new_message, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ содСрТит тСкст сообщСния, имя отправитСля ΠΈ имя Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹.

Когда ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ ΠΏΠΎΠΊΠΈΠ΄Π°Π΅Ρ‚ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ ΠΈΠ»ΠΈ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ ΠΎΡ‚ Ρ‡Π°Ρ‚Π°, ΠΊΠ»ΠΈΠ΅Π½Ρ‚ отправляСт Π½Π° сСрвСр сообщСниС disconnect, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ удаляСт ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ· всСх ΠΊΠΎΠΌΠ½Π°Ρ‚ ΠΈ отправляСт ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ сообщСния ΠΎΠ± ΡƒΡ…ΠΎΠ΄Π΅. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π΄Π°Ρ‚ΡŒ своС имя ΠΏΡ€ΠΈ Π²Ρ…ΠΎΠ΄Π΅ Π² Ρ‡Π°Ρ‚, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ сообщСниС set_username.

client.js

const socket = io();
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ"
document.getElementById('create-room').addEventListener('submit', (event) => {
	event.preventDefault();
	const roomName = document.getElementById('room-name').value;
	socket.emit('create_room', roomName);
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "ΠŸΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒΡΡ ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅"
document.getElementById('join-room').addEventListener('submit', (event) => {
	event.preventDefault();
	const roomName = document.getElementById('room-name').value;
	const username = document.getElementById('username').value;
	socket.emit('join_room', { roomName, username });
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ сообщСния Π² Ρ‚Π΅ΠΊΡƒΡ‰ΡƒΡŽ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ
document.getElementById('send-message').addEventListener('submit', (event) => {
	event.preventDefault();
	const message = document.getElementById('message').value;
	socket.emit('send_message', { roomName: currentRoom, message });
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для получСния сообщСния ΠΎΡ‚ сСрвСра ΠΎ создании Π½ΠΎΠ²ΠΎΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
socket.on('created_room', ({ roomName, participants }) => {
	console.log(`Room created: ${roomName}`);
	// добавляСм ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ Π² список доступных ΠΊΠΎΠΌΠ½Π°Ρ‚
	const roomList = document.getElementById('room-list');
	const newRoom = document.createElement('li');
	newRoom.textContent = roomName;
	newRoom.addEventListener('click', () => joinRoom(roomName));
	roomList.appendChild(newRoom);
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для получСния сообщСния ΠΎΡ‚ сСрвСра ΠΎ присоСдинСнии ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅
socket.on('room_joined', ({ roomName, username }) => {
	console.log(`${username} joined ${roomName}`);
	// ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌΡΡ Π½Π° Π½ΠΎΠ²ΡƒΡŽ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ
	switchRoom(roomName);
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для получСния сообщСния ΠΎΡ‚ сСрвСра ΠΎ присоСдинСнии Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅
socket.on('joined_room', ({ roomName, participants }) => {
	console.log(`Participants in ${roomName}: ${participants}`);
	// ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ список участников ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
	const participantList = document.getElementById('participant-list');
	participantList.innerHTML = '';
	participants.forEach((username) => {
		const newParticipant = document.createElement('li');
		newParticipant.textContent = username;
		participantList.appendChild(newParticipant);
	});
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для получСния Π½ΠΎΠ²ΠΎΠ³ΠΎ сообщСния ΠΎΡ‚ сСрвСра
socket.on('new_message', ({ roomName, message, sender }) => {
	console.log(`New message in ${roomName} from ${sender}: ${message}`);
	// добавляСм сообщСниС Π² Ρ‡Π°Ρ‚
	const chatBox = document.getElementById('chat-box');
	const newMessage = document.createElement('div');
	newMessage.classList.add('message');
	newMessage.innerHTML = `<span class="sender">${sender}</span>: ${message}`;
	chatBox.appendChild(newMessage);
});
 
// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для получСния сообщСния ΠΎΡ‚ сСрвСра ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ ΠΏΠΎΠΊΠΈΠ½ΡƒΠ» ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ
socket.on('user_left', ({ roomName, participants, username }) => {
	console.log(`${username} left ${roomName}`);
	// ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ список участников ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
	const participantList = document.getElementById('participant-list');
	participantList.innerHTML = '';
	participants.forEach((username) => {
		const newParticipant = document.createElement('li');
		newParticipant.textContent = username;
		participantList.appendChild(newParticipant);
	});
});
 
// функция для ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π½Π° Π½ΠΎΠ²ΡƒΡŽ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρƒ
function switchRoom(roomName) {
	currentRoom = roomName;
	console.log(`Switching to room: ${roomName}`);
	// ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ имя Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
	const currentRoomTitle = document.getElementById('current-room-title');
	currentRoomTitle.textContent = roomName;
	// ΠΎΡ‡ΠΈΡ‰Π°Π΅ΠΌ Ρ‡Π°Ρ‚
	const chatBox = document.getElementById('chat-box');
	chatBox.innerHTML = '';
	// Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Π΅ΠΌ список участников ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹
	socket.emit('get_participants', roomName);
}
 
// функция для присоСдинСния ΠΊ ΠΊΠΎΠΌΠ½Π°Ρ‚Π΅
function joinRoom(roomName) {
	console.log(`Joining room: ${roomName}`);
	const username = document.getElementById('username').value;
	socket.emit('join_room', { roomName, username });
}
 
// инициализация прилоТСния
let currentRoom;
switchRoom('general');