Websocket + NodeJS implements chat rooms

A, websocket

1. Websocket Introduction

The traditional HTTP protocol, is based on request and response, can not directly do the client to send messages to the client. Websocket protocol is a new network protocol based on TCP. The full-duplex communication between browser and server is realized. Full-duplex: The client can proactively send messages to the server, and the server can also proactively send messages to the client. Websocket is a persistent protocol, and HTTP is a non-persistent traditional HTTP protocol. Even if the chat is implemented, it must be done through Ajax polling, which means that the client will keep sending requests to the server to confirm whether there is a message, wasting performance and resources.

2. Use WebSocket

2.1 Using WebSocket in H5


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            border: 1px solid # 000;
        }
    </style>
</head>
<body>
<input type="text" placeholder="Please enter">
<button>send</button>

<! -- Display content -->
<div></div>

<! H5 provides the WebSocket API in the browser -->
<script>
    let input = document.querySelector('input');
    let button = document.querySelector('button');
    let div = document.querySelector('div');

    // Create WebSocket('WebSocket server address ')
    let socket = new WebSocket('ws://echo.websocket.org');
    // Listen for WebSocket event open and WebSocket server connection triggered successfully
    socket.addEventListener('open',()=>{
        div.innerHTML = 'Connection successful';
    });

    // Send a message to webSocket
    button.addEventListener('click', () = > {let value = input.value;
        socket.send(value);
    });

    // Accept webSocket service messages
    socket.addEventListener('message',(msg)=>{
        console.log(msg.data);
        // Display the message to div
        div.innerHTML = msg.data;
    });
    // Port service
    socket.addEventListener('close', () = > {console.log('Service disconnected');
    });
</script>
</body>
</html>
Copy the code

Develop WebSocket using NodeJS

Developing WebSocket with NodeJS requires a dependency package nodeJS Webs

npm i nodejs-websocket

App. Js code

const ws = require('nodejs-websocket');
const PORT = 3000

// Create a server. Every time a user connects, the callback execution creates a connect object for the user
const server = ws.createServer(connect= > {
    console.log('User connection successful');
    // The user sends data, triggering the text event
    connect.on('text', data => {
        console.log('Received user data:${data}`);
        // After receiving the data, give the user the response data
        connect.sendText(data);
    });

    // The close event is triggered when the connection is closed
    connect.on('close', () = > {console.log('Disconnected');
    });

    // Register an error event to trigger the exception after the user port is registered
    connect.on('error', () = > {console.log('User connection abnormal');
    });
});

server.listen(PORT, () => {
    console.log('listen to 3000');
});
Copy the code

Change HTML webSocket server address to ‘ws://localhost:3000/’

Running effect

3. Develop a simple chat room using WebSocket

app.js

const ws = require('nodejs-websocket');

// Record the number of connected users
let count = 0;

const server = ws.createServer(conn= > {
    console.log('New connection');
    count++;
    // Give the user a fixed name
    conn.userName = ` user${count}`;
    // Tell all users that someone has joined the chat room
    broadcast(`${conn.userName}Join a chat room);

    // This event is triggered by receiving data from the client
    conn.on('text', data => {
        // Receive a user's data, tell all users this message, broadcast
        broadcast(data);
    });
    // Close the connection
    conn.on('close', () = > {console.log('Close the connection')
        count--;
        // Let all users know if someone quit
        broadcast(`${conn.userName}Left the chat room)});// Send exception
    conn.on('error', () = > {console.log('abnormal');
    });
});

/ / radio
const broadcast = (msg) = > {
    // Server. Connection indicates all users
    server.connections.forEach(item= > {
        // Iterate over each user and send messages one by one
        item.send(msg);
    });
}

server.listen(3000, () = > {console.log('listen to 3000');
});
Copy the code

index.html


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        /*div { width: 200px; height: 200px; border: 1px solid #000; } * /
    </style>
</head>
<body>
<input type="text" placeholder="Please enter">
<button>send</button>

<! -- Display content -->
<div></div>

<! H5 provides the WebSocket API in the browser -->
<script>
    let input = document.querySelector('input');
    let button = document.querySelector('button');
    let div = document.querySelector('div');

    // Create WebSocket('WebSocket server address ')
    let socket = new WebSocket('ws://localhost:3000/');
    // Listen for WebSocket event open and WebSocket server connection triggered successfully
    socket.addEventListener('open',()=>{
        div.innerHTML = 'Connection successful';
    });

    // Send a message to webSocket
    button.addEventListener('click', () = > {let value = input.value;
        socket.send(value);
        input.value = ' ';
    });

    // Accept webSocket service messages
    socket.addEventListener('message',(msg)=>{
        console.log(msg.data);
        // Append the message to the div
        let dv = document.createElement('div');
        dv.innerHTML = msg.data;
        div.appendChild(dv);
    });
    // Port service
    socket.addEventListener('close', () = > {console.log('Service disconnected');
    });
</script>
</body>
</html>
Copy the code

Running effect

3.1. Optimize the effect of chat room messages

To optimize the message as an object:

  • Type: indicates the type of the message
    • 0: indicates messages that enter the chat room
    • 1: indicates a message leaving the chat room
    • 2: Normal chat
  • MSG: indicates the content of the message
  • Time: The specific time of the chat
  • Note: Convert this object to JSON format before sending it

app.js

const ws = require('nodejs-websocket');
// Enter the message
const TYPE_ENTER = 0;
// Leave the message
const TYPE_LEAVE = 1;
// Normal message
const TYPE_MSG = 2;

// Record the number of connected users
let count = 0;

const server = ws.createServer(conn= > {
    console.log('New connection');
    count++;
    // Give the user a fixed name
    conn.userName = ` user${count}`;
    // Tell all users that someone has joined the chat room
    broadcast({
        type: TYPE_ENTER,
        msg: `${conn.userName}Join a chat room.time: new Date().toLocaleDateString()
    });

    // This event is triggered by receiving data from the client
    conn.on('text', data => {
        // Receive a user's data, tell all users this message, broadcast
        broadcast({
            type: TYPE_MSG,
            msg: data,
            time: new Date().toLocaleDateString()
        });
    });
    // Close the connection
    conn.on('close', () = > {console.log('Close the connection')
        count--;
        // Let all users know if someone quit
        broadcast({
            type: TYPE_LEAVE,
            msg:  `${conn.userName}Left the chat room.time: new Date().toLocaleDateString()
        })
    });
    // Send exception
    conn.on('error', () = > {console.log('abnormal');
    });
});

/ / radio
const broadcast = (msg) = > {
    // Server. Connection indicates all users
    server.connections.forEach(item= > {
        // Iterate over each user and send messages one by one
        item.send(JSON.stringify(msg));
    });
}

server.listen(3000, () = > {console.log('listen to 3000');
});
Copy the code

The change of the HTML

 const TYPE_ENTER = 0;
 const TYPE_LEAVE = 1;
 const TYPE_MSG = 2;
// Accept webSocket service messages
    socket.addEventListener('message',(e)=>{
        let data = JSON.parse(e.data);
        // Append the message to the div
        let dv = document.createElement('div');
        // Add effects for different message types
        dv.innerHTML = data.msg + The '-' + data.time;
        if (data.type === TYPE_ENTER) {
            dv.style.color = 'green';
        } else if (data.type === TYPE_LEAVE){
            dv.style.color = 'red';
        } else {
            dv.style.color = 'blue';
        }
        div.appendChild(dv);
    });
Copy the code

Running effect

2. Basic usage of socketio

2.1. No frame

Socket.IO is a library for real-time, bidirectional, and event-based communication between browsers and servers. Socket.IO is not an implementation of WebSocket. Install socketio

npm i –save socket.io

app.js

// Create the server
const http = require('http');
const fs = require('fs');
const app = http.createServer();


// Listen for the request event and return index.html when the service is requested
app.on('request', (req, res) => {
    fs.readFile(__dirname + '/index.html',
        (err, data) => {
            if (err) {
                res.writeHead(500);
                return res.end('Error loading index.html');
            }

            res.writeHead(200);
            res.end(data);
        });
})

app.listen(3000, () = > {console.log('listen to 3000');
});

// Socketio depends on the NodeJS server
const io = require('socket.io')(app);
// Listen for user connection events
//socket indicates the connection of the user
// Socket emit triggers an event. If you send data to the browser, you need to trigger an event registered by the browser
// Socket on registers an event. If you need to obtain browser data, you need to register an event and wait for the browser to trigger it
io.on('connection', socket => {
    console.log('New user Connection');
    // Emit data to the browser (' sent event ',' sent event ')
    socket.emit('send', {name: 'jack'});

    // Get the data sent by the browser and register the event as long as it is the same as the event that was triggered
    socket.on('clientData',data=>{
        console.log(data); })});Copy the code

index.html


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
hello
<script src="/socket.io/socket.io.js"></script>
<script>
    // Connect to the socket service
    let socket = io('http://localhost:3000');
    // The browser registers the server
    socket.on('send',data =>{
        console.log(data);
    });

    // Send data to the server
    socket.emit('clientData', {name:'jack'});
</script>
</body>
</html>
Copy the code

Send data using Emit, accept data using ON. When the browser or server sends data to the other party, it only needs to trigger the corresponding event.

2.2 Socketio based on express framework

Install express

npm i express

app.js

const app = require('express') ();const server = require('http').Server(app);
const io = require('socket.io')(server);

server.listen(3000, () = > {console.log('listen to 3000');
});

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

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

    socket.on('clientData', (data) => {
        console.log(data);
    });
});
Copy the code

index.html


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
hello
<script src="/socket.io/socket.io.js"></script>
<script>
    let socket = io('http://localhost:3000');
        socket.emit('clientData', { name: 'jack' });
</script>
</body>
</html>
Copy the code

Use socketio to develop chat rooms

Import a static resource directory structure

1. Set up the server

app.js

/* Start the chat room server program */
const app = require('express') ();const server = require('http').Server(app);
const io = require('socket.io')(server);

server.listen(3000, () = > {console.log('Server started successfully');
});

// Handle static resources and set the public directory to static resources
app.use(require('express').static('public'));

app.get('/', (req, res) => {
    // Redirect to the home page
    res.redirect('/index.html');
});

io.on('connection', (socket) => {
   console.log('New user Connection');
});
Copy the code

2. Login function

2.1 Login verification

js/index.js

/* Main functions of the chat room */

/ / socketio connection
let socket = io('http://localhost:3000');

/* Login function */

// Select the profile picture and register a click event with Li. Add now class addClass and siblings and remove the siblings from other Li classes
$('#login_avatar li').on('click'.function () {$(this).addClass('now').siblings().removeClass('now');
});

// Click the button to log in
$('#loginBtn').on('click'.function () {
    // Get the user name, remove the space
    let username = $('#username').val().trim();
    if(! username) { alert('Please enter a user name');
        return
    }
    Now select li attr(' SRC ') to get the profile picture path
    let avatar = $('#login_avatar li.now img').attr('src');

    // Communicate with the server and transfer the user name and profile picture to the server
    socket.emit('login', {username: username,
        avatar: avatar
    });
});

// Listen for failed login requests
socket.on('loginError',data=>{
    alert('Login failed, user name already exists');
});
// Listen for successful login requests
socket.on('loginSuccess',data=>{
    alert('Login successful');
});

Copy the code

app.js

// Save all users who have logged in
const users = [];
// Listen for user connections
io.on('connection', (socket) => {
   console.log('New user Connection');
   // Listen for login requests sent by the client
   socket.on('login',data=>{
       // Check whether the name is the same as the name in the array
       let user = users.find(item= > item.username === data.username);
       if (user) {
           // The user exists. Login failed
            socket.emit('loginError', {msg:'Login failed'});
       }else {
           // If the user does not exist, save the user to the array
           users.push(data);
           socket.emit('loginSuccess',data); }}); });Copy the code

2.2 Personal information is displayed after login

js/index.js

// Listen for successful login requests
socket.on('loginSuccess',data=>{
    // show the chat box, hide the chat window fadeOut,fadeIn fadeIn effect
    $('.login_box').fadeOut();
    $('.container').fadeIn();
    // Set the user information after successful login
    $('.avatar_url').attr('src',data.avatar);
    $('.user-list .username').text(data.username);
});
Copy the code

2.3. Broadcast new users joining a group chat

app.js

// Listen for user connections
io.on('connection', (socket) => {
   console.log('New user Connection');
   // Listen for login requests sent by the client
   socket.on('login',data=>{
       // Check whether the name is the same as the name in the array
       let user = users.find(item= > item.username === data.username);
       if (user) {
           // The user exists. Login failed
            socket.emit('loginError', {msg:'Login failed'});
       }else {
           // If the user does not exist, save the user to the array
           users.push(data);
           socket.emit('loginSuccess',data);

           // broadcast message, someone has joined the chat room IO. Emit broadcast event
           io.emit('addUser',data); }}); });Copy the code

js/index.js

// Listen for requests for new users to join
socket.on('addUser', data => {
    // Add a system message
    $('.box-bd').append(
        `<div class="system">
                <p class="message_system">
                    <span class="content">${data.username}</span> </p> </div> ')});Copy the code

Display user list and user number

app.js

// Listen for user connections
io.on('connection', (socket) => {
   console.log('New user Connection');
   // Listen for login requests sent by the client
   socket.on('login',data=>{
       // Check whether the name is the same as the name in the array
       let user = users.find(item= > item.username === data.username);
       if (user) {
           // The user exists. Login failed
            socket.emit('loginError', {msg:'Login failed'});
       }else {
           // If the user does not exist, save the user to the array
           users.push(data);
           socket.emit('loginSuccess',data);

           // broadcast message, someone has joined the chat room IO. Emit broadcast event
           io.emit('addUser',data);

           // Displays the current number of people in the chat room
           io.emit('userList',users); }}); });Copy the code

js/index.js

/ Listen for user list messages socket.on('userList', data => {
    // Display the data in userList dynamically in the left menu
    $('.user-list ul').html(' ');    // Set the list to null at the end of each loop, otherwise the stack will repeat
    data.forEach(item= >{$('.user-list ul').append(
            `<li class="user">
                <div class="avatar"><img src="${item.avatar}" alt=""/></div>
                <div class="name">${item.username}</div>
            </li>`)})// Display the number of users
    $('#userCount').text(data.length);
});
Copy the code

3, leave the chat room function

app.js

// Listen for user disconnect disconnect event
    socket.on('disconnect', () = > {// Delete the current user from users. FindIndex finds the subscript of the current user
        let idx = users.findIndex(item= > item.username === socket.username);
        / / delete
        users.splice(idx,1);
        // Broadcast someone leaving the chat room
        io.emit('delUser', {username:socket.username,
            avatar: socket.avatar
        })

        / / update the userList
        io.emit('userList', users);
    })
Copy the code

js/index.js

// Listen for delete user messages
socket.on('delUser', data => {
    // Add a system message
    $('.box-bd').append(
        `<div class="system">
                <p class="message_system">
                    <span class="content">${data.username}< span style = "box-sizing: border-box! Important; word-wrap: break-word! Important)});Copy the code

4. Chat function

4.1 Implementation of basic chat function

app.js

// Listen to chat messages
    socket.on('sendMessage',data=>{
        console.log(data);
        // Broadcast the message if there is a database where the data is stored
        io.emit('receiveMessage',data);
    })
Copy the code

js/index.js

// Define two global variables to hold username and Avatar
var username;
var avatar;

// Save the user information after the successful login
 username = data.username;
    avatar = data.avatar;
// Chat function
$('.btn-send').on('click', () = > {// Get the chat content
    let content = $('#content').val().trim();
    // Empty the chat area
    $('#content').val(' ');
    // Check whether the data is empty
    if(! content) {return alert('Please enter content');
    }
    // Send to the server
    socket.emit('sendMessage', {
        msg: content,
        username: username,
        avatar: avatar
    })
})

// Listen to chat messages
socket.on('receiveMessage', data => {
    // Display the received message in the chat window
    // Determine whether the information is your own or someone else's
    if (data.username === username) {
        // Your own message
        $('.box-bd').append(
            ` <div class="message-box">
                <div class="my message">
                    <img class="avatar" src="${data.avatar}" alt=""/>
                    <div class="content">
                        <div class="bubble">
                            <div class="bubble_cont">${data.msg}</div>
                        </div>
                    </div>
                </div>
            </div>`
        );
    } else {
        // Other people's news
        $('.box-bd').append(
            `<div class="message-box">
                <div class="other message">
                    <img class="avatar" src="${data.avatar}" alt=""/>
                    <div class="content">
                        <div class="nickname">${data.username}</div>
                        <div class="bubble">
                            <div class="bubble_cont">${data.msg}</div>
                        </div>
                    </div>
                </div>
            </div>`); }})Copy the code

4.2. Automatically scroll to the bottom after sending the message

The element.scrollintoView () method lets the current Element scroll into the viewable area of the browser window.

  • If true, the top of the element is aligned with the top of the visible area of the scroll where it is located.
  • If false, the bottom of the element is aligned with the bottom of the visible area of the scroll where it is located.

js/index.js

function scrollIntoView() {
    // Use scrollIntoView() to scroll to the bottom,children(':last') to find the last child, and get(0) to get the ELEMENT's DOM object
    $('.box-bd').children(':last').get(0).scrollIntoView(false);
}
Copy the code

Every listener that sends a message uses the scrollIntoView method, including system notifications

4.3 Send pictures

app.js

// Send the image change to indicate the selection of the file
$('#file').on('change'.function () {
    // Get the uploaded file
    var file = this.files[0];
    // Send the file to the server, use H5 function fileReader, read the uploaded file
    var fr = new FileReader();
    fr.readAsDataURL(file);
    fr.onload = function () {
        socket.emit('sendImage', {
            username: username,
            avatar: avatar,
            img: fr.result
        })
    }
})

// Listen for pictures
socket.on('receiveImage', data => {
    // Display the received message in the chat window
    // Determine whether the information is your own or someone else's
    if (data.username === username) {
        // Your own message
        $('.box-bd').append(
            ` <div class="message-box">
                <div class="my message">
                    <img class="avatar" src="${data.avatar}" alt=""/>
                    <div class="content">
                        <div class="bubble">
                            <div class="bubble_cont">
                                <img src="${data.img}">
                            </div>
                        </div>
                    </div>
                </div>
            </div>`
        );
    } else {
        // Other people's news
        $('.box-bd').append(
            `<div class="message-box">
                <div class="other message">
                    <img class="avatar" src="${data.avatar}" alt=""/>
                    <div class="content">
                        <div class="nickname">${data.username}</div>
                        <div class="bubble">
                            <div class="bubble_cont">
                                <img src="${data.img}">
                            </div>
                        </div>
                    </div>
                </div>
            </div>`
        );
    }
    // Wait for the image to load before scrolling
    $('.box-bd img:last').on('load'.function () {
        scrollIntoView();
    });

});
Copy the code

js/index.js

 // Accept the picture information
    socket.on('sendImage',data=>{
        // Broadcast the message if there is a database where the data is stored
        io.emit('receiveImage',data);
    })
Copy the code

4.3. Sending emojis

Using Jquery emoji plugin

Introduce CSS in index.html

<link rel="stylesheet" href="lib/jquery-mCustomScrollbar/css/jquery.mCustomScrollbar.min.css"/>
<link rel="stylesheet" href="lib/jquery-emoji/css/jquery.emoji.css"/>
Copy the code

The introduction of js

<script src="lib/jquery-mCustomScrollbar/script/jquery.mCustomScrollbar.min.js"></script>
<script src="lib/jquery-emoji/js/jquery.emoji.min.js"></script>
Copy the code

Emoji () parameter reference

js/index.js

// Send emojis
// Initialize jQuery-emoji plugin
$('.face').on('click'.function () {$('#content').emoji({
        // The button that triggers the emoji
        button: '.face'.showTab: false.animation: 'slide'.position: 'topRight'.icons: [{
            name: "QQ expression".path: ".. /lib/jquery-emoji/img/qq/".maxNum: 91.excludeNums: [41.45.54].file: ".gif"}}]); })Copy the code

Note: < div > < div style = “color: RGB (50, 50, 50); If you can’t get the contents of the div using the original val, use HTML () instead

Screenshot of project running

GitHub: github.com/812467457/w…