1

I implemented chat using node & socket.io. It has server and client code.

chatApp.js

"use strict";
let app = require('express')();
let server = require('http').Server(app);
let io = require('socket.io')(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + "/logs/chat2.html");
});

io.on("connect", (socket) => {
  // New user
  socket.emit("userSelfNotification", {text: "--SELF-- You joined server"});
  socket.broadcast.emit("newUserNotification", {text: "--BROADCAST-- New user"});
  // Send sms
  socket.on("message", (message) => {
    io.sockets.emit("newMessage", {text: message});
  });
  // Change room
  socket.on("changeRoom", (room) => {
    socket.room = room;
    socket.broadcast.emit("leaveRoom", {text: "--BROADCAST-- user left "});
    socket.join(room);
    socket.in(room).emit("roomChangeNotification", {text: "--ROOM-- You left"});
    io.sockets.in(room).emit("newUserNotification", {text: "--ROOM_ALL-- New user joined"});
  });
});

server.listen(3000);

client.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Чат</title>
    <script src="https://code.jquery.com/jquery-3.1.0.min.js" charset="utf-8"></script>
    <script src="http://localhost:3000/socket.io/socket.io.js"></script>
    <script>
      let socket = io('http://localhost:3000/');
      socket.on("userSelfNotification", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
      socket.on("newUserNotification", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
      socket.on("newMessage", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
      socket.on("leaveRoom", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
      socket.on("roomChangeNotification", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
      socket.on("newUserNotification", (message) => {
        $("textarea").val($("textarea").val() + message.text + "\n");
      });
    </script>
</head>
<body>
<textarea name="name" rows="15" cols="50"></textarea>
<p></p>
<input type="text" name="text" size="20">
<button type="button" name="button">Отправить</button>
<button type="button" name="changeRoomButton">Сменить комнату</button>
<script>
  $(document).on('click', 'button', () => {
    let message = $('input').val();
    socket.emit("message", message);
    $('input').val(null);
  });
  $(document).ready(function(){
    $("[name='changeRoomButton']").click(function(){
      $(".test").hide();
      console.log("I work babeee");
      socket.emit("changeRoom", "room");
    });
  });
</script>    
</body>
</html>

The main problems is that I cannot implement room change: even if user changed the room, he/she still can write to other default room and read other messages from it. Also, some events are emitted in both rooms.

Help me understand where is my fault, thanks!

Evgeny Kobzev
  • 89
  • 1
  • 12

2 Answers2

2

Since a socket client can be subscribed to multiple rooms, if you want to only have your clients to only be able to be in one room at a time, then you will have to do a .leave in the when joining the new one.

The actual way to access the roomNames that a client is subscribed to is by using: socket.rooms

But since you are not subscribing (join) / unsuscribing (leave) to many channels (rooms) and only having one active room at a time, you can call : socket.leaveAll and then do socket.join(room)

If for example you want to notify on client disconnection you have to do it on disconnecting because on disconnect leaveAll() has already been called :

socket.on('disconnect', function(){(
    /*
      socket.rooms is empty here 
      leaveAll() has already been called
    */
 });
 socket.on('disconnecting', function(){
   // socket.rooms should isn't empty here 
   var rooms = socket.rooms.slice();
   /*
     here you can iterate over the rooms and emit to each
     of those rooms where the disconnecting user was. 
   */
 });

Socket.IO: How to correctly join and leave rooms

Socket.IO: Emit Cheat Sheet

EMX
  • 6,066
  • 1
  • 25
  • 32
0

Thanks for the answers: every seemed to be semi-correct, but too abstract. I solved the puzzle by reassigning "socket.room" to the room I wish joining. This block solved the problem:

// Change room
socket.on("changeRoom", (room) => {
    socket.in(defaultRoom).broadcast.emit("leaveRoom", {text: "--BROADCAST-- User left room"});
    socket.leave(defaultRoom);
    socket.room = room;
    socket.join(room);
    socket.emit("roomChangeNotification", {text: "--ROOM-- You changed room"});
    socket.in(room).broadcast.emit("OnewUserNotification", {text: "--ROOM_ALL-- New user joined"});
  });

SO: 1. I added default room and auto joined every user that connects to server 2. I added code that reassignes socket.room parameter from "defaultRoom" to my custom room named "room". That´s it.

Evgeny Kobzev
  • 89
  • 1
  • 12
  • That is not really good.... Its always leaving the defaultRoom (the one you have left already in your first change) everytime you are changing room `socket.leave(defaultRoom);` and you are not really leaving the new rooms you join, so if you are going to use this strategy you will need to do `socket.room = defaultRoom` (on connection) and then in the changeRoom event you have to modify `socket.leave(defaultRoom);` for `socket.leave(socket.room);` – EMX Aug 25 '17 at 22:35
  • 1
    It would be much easier like I state in my answer to use `socket.leaveAll` and then `socket.join(room)` (since you only want your clients to be subscribed to one room at a time) – EMX Aug 25 '17 at 22:37