js+node.js+socket.io realizes chat function (private chat, group chat creation)

Keywords: Javascript socket npm Firefox Google

Design sketch:

Four clients are started for testing

1. Log in and get the list of online users

2. Private chat function

3. Group chat function

I found WebSocket by chance. I found that it can communicate in real time and chat online. So I made a demo of chat tool and recorded it

Source code

Socket.io

WebSocket is native to js, and Socket.io is a framework to encapsulate WebSocket

Official website description:

introduce

Socket.io is a WebSocket library, including js of the client and nodejs of the server. Its goal is to build real-time applications that can be used on different browsers and mobile devices. It will automatically select the best way to realize the real-time application of the network from WebSocket, AJAX long polling, Iframe flow and other ways according to the browser, which is very convenient and humanized, and the browser supported is as low as IE5.5

socket.io features

Real time analysis: push data to clients, which will be represented as real-time counters, charts or log clients.

Real time communication and chat: only a few lines of code can be written into a Socket.IO "Hello,World" chat application.

Binary streaming: since version 1.0, Socket.IO supports any form of binary file transmission, such as picture, video, audio, etc.

Document merging: allows multiple users to edit a document at the same time, and can see the changes made by each user.

Official document in Chinese

Official documents in English

directory structure

Create a new folder - > npm init - y generate package.json you can use npm to install plug-ins

Using npm to install express, socket.io

npm install express --save 
npm install socket.io --save

package.json installed

{
  "name": "websocketchat",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "socket.io": "^2.3.0"
  }
}

connection and disconnect

Here's just an example of connecting, disconnecting, and receiving messages, not included in the project

These two events are built-in events of the framework itself

connection listens for client connections

Disconnect listening client disconnect

Client code

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- Introduce socket.io -->
    <script src="/socket.io/socket.io.js"></script>
</head>
<body></body>
<script>
    window.socket = io();
    socket.on('connect', () => {
        window.socket.on('success', data => {
            console.log(data)
        })

        window.socket.on('quit', (id) => {
            console.log(`${id}Connection disconnect`)
        })
    })
</script>
</html>

Server code

server.js

const fs = require('fs');
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require("socket.io")(http);
// Route as / default www static folder
app.use('/', express.static(__dirname + '/src'));

io.on('connection', socket => {
    socket.emit('success', 'Connect to server')

    socket.on('disconnect', () => {
        io.emit('quit', socket.id)
    })
})

http.listen(3002, () => {
    console.log('http://localhost:3002/index.html')
})

Start the server and run node server.js

Browser access http://localhost:3002/index.html

Browser console output: connecting to the server

Pay attention to the character set settings of the editor, otherwise garbled code may appear

You can open two browsers, Google and Firefox respectively http://localhost:3002/index.html

Then turn off the access page of Firefox, you can see the effect of disconnection. Google console output: TgkBeWIJK7G4hwlJAAAC disconnection

The server code listens for the disconnect event and sends the message io.emit instead of socket.emit for the following reasons:

io.emit() broadcasts messages to all clients
 socket.emit() sends a message to the client of the socket

Firefox turns off the access page. Socket. Exit ('Quit ', socket. ID) is equivalent to sending messages to the Firefox client. But this page has been turned off, so it is not visible
 io.emit('quit', socket.id) broadcasts messages to all clients, so Google browser can also receive this message

or

Use socket.broadcast.emit('quit', socket.id);
socket.broadcast.emit() broadcasts to all socket connections, but does not include the sender itself

The function and code of this project are introduced below

Log in and get a list of online users

Client main.js

function Chat() {
    this.userName // Current login user name;
    this.userImg; // User head
    this.id; // User socket ID each client has its own socket.id through which private chat can be realized
    this.userList = []; // Friends list
    this.chatGroupList = []; // Group chat list
    this.sendFriend = ''; // socketId of the user who is currently chatting with a friend
    this.sendChatGroup = ''; // The roomId of the group chat currently being chatted
    this.messageJson = {}; // Friends message list
    this.msgGroupJson = {}; // Group chat message list
    this.tag = 0; // 0 my friends panel 1 group chat panel
}
Chat.prototype = {
    init() {
        this.userName = localStorage.getItem('userName');
        this.userImg = localStorage.getItem('userImg');
        this.selectClick(); // Registration page button click event
        this.setAllPorarait(); // Add avatars, pictures and emoticons on the page
        // There is a user name in the cache, and the avatar does not need to be entered again
        if (this.userName && this.userImg) {
            $("#login-wrap").style.display = 'none';
            this.login(this.userName, this.userImg);
        } else {
            $('.chat-btn').onclick = () => {
                let userName = $('.user-name').value;
                let userImg = $('.my-por').getAttribute('src');
                this.login(userName, userImg);
            }
        }
    },
    login(userName, userImg) {
        if (userName && userImg) {
            this.initSocket(userName, userImg);
        }
    },
    initSocket(userName, userImg) {
        window.socket = io();
        window.socket.on('connect', () => {
            $("#login-wrap").style.display = 'none';
            $('.chat-panel').style.display = 'block';
            this.userName = userName;
            this.userImg = userImg;
            this.id =  window.socket.id; // The id can only be obtained after the connection is successful. Every time the browser is refreshed, a new id will be obtained
            let userInfo = {
                id:  window.socket.id,
                userName: userName,
                userImg: userImg
            }
            // Get user name, avatar, and socketid to set cache and send to server
            localStorage.setItem('userName', userName);
            localStorage.setItem('userImg', userImg);
            window.socket.emit('login', userInfo);
        })
        window.socket.on('userList', (userList) => {
            this.userList = userList; // Return to all current online users
            this.drawUserList(); // Draw friends list
        })

        window.socket.on('quit', (id) => {
            this.userList = this.userList.filter(item => item.id != id)
            this.drawUserList();
        })
    }
    
}

server.js

let userList = [];
io.on('connection', (socket) => {
    // The front-end socket.emit('login ') sends messages and the back-end socket.on('login') receives them
    socket.on('login', (userInfo) => {
        userList.push(userInfo);
        io.emit('userList', userList);
        /*  io.emit(Broadcast messages to all clients)=
            socket.emit(Send message to the client of the socket) + socket.broadcast.emit (to all clients, excluding themselves)
        */
    })
    
    // Exit (built-in event)
    socket.on('disconnect', () => {
        userList = userList.filter(item => item.id != socket.id)
        io.emit('quit', socket.id)
    })
})

Private chat

Process client A, client B private chat

  1. Click B in the friend list of client A to open the chat panel, mark the current chat object, this.sendFriend = id of the current chat object
  2. A input the content, click the send button, use window.socket.emit('sendMsg', data) to send a message to the server with the id of the client receiving the message
Client main.js
//Message sending button click event:
let info = {
    sendId: this.id, // Sender id
    id: this.sendFriend, // Receiver id
    userName: this.userName, // Sender user name
    img: this.userImg, // Sender's head
    msg: $('.inp').innerHTML // send content
}
window.socket.emit('sendMsg', info)
  1. Server socket. On ('sendmsg ', (data) = > {}) receives this message, socket.to(data.id).emit('receiveMsg', data)
socket.on('sendMsg', (data) => {
    socket.to(data.id).emit('receiveMsg', data)
})
  1. The client socket.on('receiveMsg', callback) corresponding to id receives the private chat message
window.socket.on('receiveMsg', data => {
    this.setMessageJson(data); // Add this message to message list data
    // Determine whether the sendid of this message is the currently chatting object
    // true page to draw chat message
    if (data.sendId === this.sendFriend) {
        this.drawMessageList(); // Page drawing chat message
    } else {
        // false the red dot is displayed in the top left corner of a friend's picture, indicating that the friend has sent a new message
        $('.me_' + data.sendId).innerHTML = parseInt($('.me_' + data.sendId).innerHTML) + 1;
        $('.me_' + data.sendId).style.display = 'block';
    }
})

Group chat

Creating group chat

  1. Click the create group chat button - > all online users list appears - > click Select - > Enter group name - > confirm creation - > the client sends the create group chat message to the server
Client main.js

window.socket.emit('createChatGroup', {
    masterId: chat.id, // Creator id
    masterName: chat.userName, // Creator user name
    // Room id: you can set room id splicing rules by yourself, which is different from the user's socketid
    // The user's socket ID is obtained by socket.id, and the room ID is self-defined splicing, as long as it is guaranteed not to be repeated
    roomId: 'room_' + chat.id + (Date.now()), 
    chatGroupName: $('.chatGroupNameInput').value, // Group name
    member: chat.chatGroupArr // Group members, including creators
})
  1. The server receives the create group chat message sent by the client

    2.1 add the client, that is, the creator, to the group chat socket.join(data.roomId);

    2.2 store the chat data chatGroupList[data.roomId] = data;
    2.3 send the message io.to(item.id).emit('chatGroupList', data) and the current group chat data to all members of the group chat

       io.to(item.id).emit('createChatGroup', data)
The server server.js

let chatGroupList = {};
// Creating group chat
socket.on('createChatGroup', data => {
    socket.join(data.roomId);
    chatGroupList[data.roomId] = data;  // Group chat list data
    // Each member of group chat sends chatgrouplist (current group chat data) and createchat group (create group chat) messages
    data.member.forEach(item => {
        io.to(item.id).emit('chatGroupList', data)
        io.to(item.id).emit('createChatGroup', data)
    });
})
  1. The client receives the chat group list message, adds the data to the group chat list, and draws the group chat list on the page;
Client main.js

window.socket.on('chatGroupList', chatGroup => {
    this.chatGroupList.push(chatGroup);
    this.drawChatGroupList(); // Draw group chat list
})
  1. The client receives the createChatGroup message and sends a message to the server saying that I want to join the group chat socket.emit('joinChatGroup', data)
Client main.js

window.socket.on('createChatGroup', (data) => {
    socket.emit('joinChatGroup', {
        id: this.id,
        userName: this.userName,
        info: data
    })
})
  1. The server receives the joinChatGroup message, adds the current client to the group chat socket.join(data.info.roomId), and informs all the people in the group chat that xxx has joined the group chat
The server server.js

// Join group chat
socket.on('joinChatGroup', data => {
    socket.join(data.info.roomId);
    io.to(data.info.roomId).emit('chatGrSystemNotice', {
        roomId: data.info.roomId,
        msg: data.userName+'Joined the group chat!',
        system: true
    });//Send messages for all socket s in the room, including yourself
})
  1. The client receives the chatGrSystemNotice system message, stores the data and draws it on the page
Client main.js

window.socket.on('createChatGroup', (data) => {
    // The client sends a message to the server that I want to join the group chat
    socket.emit('joinChatGroup', {
        id: this.id,
        userName: this.userName,
        info: data
    })
})

Group chat

The process is similar to private chat. The message sending object changes from personal id to room id

Technological process:

  1. Client A selects group chat B in the group chat list, marks the group chat roomid (this. Sendchat group) of the current chat, click to open the chat panel, enter the message, click send, window. Socket. Emit ('sendmsg Group ', info)
  2. Server received sendMsgGroup
socket.on('sendMsgGroup', (data) => {
    socket.to(data.roomId).emit('receiveMsgGroup', data);
})
  1. Client receiveMsgGroup event
window.socket.on('receiveMsgGroup', (data) => {
    this.setMsgGroupJson(data); // This message is added to the chat data list
    // Judge whether the received is the current group chat, mark the red dot or draw the chat content
    if (data.roomId === this.sendChatGroup) {
        this.drawChatGroupMsgList(); // Draw chat content
    } else {
        $('.me_' + data.roomId).innerHTML = parseInt($('.me_' + data.roomId).innerHTML) + 1;
        $('.me_' + data.roomId).style.display = 'block';
    }
})

Quit group chat

  1. Client A selects group chat B in its group chat list and clicks exit. Client sends message
window.socket.emit('leave', {
    roomId: roomId,
    id: this.id,
    userName: this.userName
})
  1. Server handles leave exit group chat event
socket.on('leave', data => {
    socket.leave(data.roomId, () => {
        let member = chatGroupList[data.roomId].member;
        let i = -1;
        // Send xx leave notification message to each member of group chat, including the leaver
        // Then delete the person who left in the member array member
        member.forEach((item, index) => {
            if (item.id === socket.id) {
                i = index;
            }
            io.to(item.id).emit('leaveChatGroup', {
                id: socket.id, // Exit group chat id
                roomId: data.roomId,
                msg: data.userName+'Left group chat!',
                system: true
            })
        });
        if (i !== -1) {
            member.splice(i)
        }
    });
})
// socket.leave() official website description:
socket.leave(room [, callback])
 * room (String)
 * callback (Function)
 * Socket Link return
//Remove client room from and optionally start a callback with err signature, if any.
//The room closes automatically after disconnection.
  1. Client leaveChatGroup
window.socket.on('leaveChatGroup', data => {
    // Current client exits group chat
    if (data.id === this.id) {
        this.chatGroupList = this.chatGroupList.filter(item => item.roomId !== data.roomId)
        this.drawChatGroupList();
    } else {
        // Other members leave group chat, and message notification will be displayed here
        this.setMsgGroupJson(data);
        if (this.tag) {
            $('.me_' + data.roomId).innerHTML = parseInt($('.me_' + data.roomId).innerHTML) + 1;
            $('.me_' + data.roomId).style.display = 'block';
            this.drawChatGroupMsgList();
        } else {
            $('.me-group-chat-tab').innerHTML = parseInt($('.me-group-chat-tab').innerHTML) + 1;
            $('.me-group-chat-tab').style.display = 'block';

            $('.me_' + data.roomId).innerHTML = parseInt($('.me_' + data.roomId).innerHTML) + 1;
            $('.me_' + data.roomId).style.display = 'block';
        }
    }
})

Little knowledge points

Remove input Click the blue border that appears when entering: outline: none;  
<div class="inp inp-box" contenteditable></div> Realization input And you can specify the width and height 

//Browser notification:
// Get permission
if (Notification && Notification.requestPermission){
    Notification.requestPermission()
}
new Notification('New news', {
    body: `${data.userName}: ${data.msg}`,
    icon: data.userImg
})

//Font scaling
font-size: 12px;
transform: scale(0.9);
display: inline-block;

There are still some imperfections in this project, such as:

  1. The friends list is directly obtained from online users. You can apply for adding friends like group chat, and the other party agrees
  2. The user's group chat list is not stored. If you refresh the browser, you need to re create the group chat
  3. No browser notification for new messages
  4. ......

github source code

Reference resources

Official document in Chinese

In short (YouChat) Thank you guys.

Posted by slamMan on Thu, 07 Nov 2019 00:24:35 -0800