Use Node and Socket to implement online chat rooms

Introduction to the

In H5 end through socket to achieve the function of online chat room, server based on Node.

This tutorial is intended to simplify the implementation process of socket and Node. So the H5 static page development step is skipped.

Static pages

Here as far as possible with the simplest code to achieve the development of a static page, the whole page is divided into two parts:

1. Chat window. Focus on the data structure of the objects in the chat object list

parameter instructions type
id string | number
name The user name string
type The message type can be text, picture, or expression text | img
message The message content

2, the bottom input box.

Branch: feat- h5-static-Page Branch of the static page

Second, establish a connection

node

In this demo, the function of the server is to transfer messages. Send the message sent by Xiaoming to everyone through the Node socket. The message structure and content can remain unchanged.

At present, there are two libraries used by developers: socket. IO and WS.

  • socket.ioThe domain name ishttp://
  • wsThe domain name isws://

So socket. IO meets the needs of actual projects. Here’s the socket. IO to implement the demo.

Project initialization

mkdir nodePkg 

cd nodePkg

yarn init

yarn add express
yarn add node
yarn add socket.io
Copy the code

Create a new folder to hold our Node project, and after the project is initialized, add a few libraries to use.

Create a new server.js file to implement the functionality.

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');

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

io.on('connection'.function (socket) {
  console.log(socket);
});

server.listen(3000);

app.listen(8080.'localhost'._= > {
  console.log('Demo server started, access address http://localhost:8080')})Copy the code
  • connectionWhen a client initiates a connection, the server can passconnectionEstablish a connection.
  • server.listenSocket connection port: 3000
  • app.listen(8080, '', () => {})The node service

Start the service

node server.js 
Copy the code

After writing the server, we now develop the Web side to test whether the connection can be successfully established.

web

Pull the code from the H5 branch above and install the required libraries.

yarn add socket.io-client
Copy the code

Write the SRC/pages/index/index. The benchmark, the following code as the thumbnail content

import React, { FC, useEffect, useState } from 'react';
import io from 'socket.io-client';

const HomePage: FC<HomePageProps> = ({ location }) = > {
  const [socket] = useState<any>(
    io('http://localhost:3000', { transports: ['websocket']}));console.log(socket);
}
Copy the code

We may find a connection failure when we start the project Web project. Don’t worry. It takes time for the Node socket to start for the first time.

When we see the Console on the Web side printing connected: true, and the Node server on(‘connection’) has a printed message on the terminal. The connection has been successfully established.

Branch: feat-connect Establish a connection branch

Send and receive messages

node

There are omitted parts of repeated code

. io.on('connection'.function (socket) {
  socket.on('message'.function (data) {
    console.log('Server receives:', data);
    socket.send(data);
  });
  socket.on('error'.function (err) {
    console.log(err); }); }); .Copy the code
  • socket.on('message'Here,messageThe value must be consistent with that on the Web server so that the two ends can communicate with each other.
  • socket.send(data);The received message is forwarded again.

web

. useEffect(() = > {
  socket.on('message'.(msg: any) = > {
      console.log(msg);
      setChatList([...chatList, msg]);
    });
}, [socket]);

const sendMessage = () = > {
  socket.emit('message', {
    id: random(),
    name,
    type: 'text'.message: inputValue, }); }; .Copy the code
  • socket.onThe Web end receives socket messages. Procedure
  • socket.emitSend a socket message.'message'Be consistent so that both ends can communicate with each other.

Restart the node service to check the effect.

Ok, so now we have successfully established the connection. But a careful observer will notice:

  • insocket.on('message'There’s no way to get it in real timechatListThe list cannot be appended.
  • At this time, if the Web side opens another page to send messages, the two pages do not communicate with each other.

Multiplayer chat rooms

node

The last legacy problem exists two pages connected to the same socket does not communicate with the problem, together to solve the ~

If you look at the current node code, you will find that each socket only receives and sends data to the current socket after establishing a connection. Therefore, different sockets cannot communicate with each other.

The solution is: we store all socket connections, and each time a socket receives a message, we iterate over each socket and send the message.

let socketSet = new Set(a);/ / store the socket

io.on('connection'.function (socket) {
  socketSet.add(socket);
  socket.on('message'.function (data) {
    socketSet.forEach(ws= > {
      if (ws.connected) { // Check whether the current socket is connected
        ws.send(data);
      } else{ socketSet.delete(ws); }})}); socket.on('error'.function (err) {
    console.log(err);
  });
});
Copy the code

Let’s verify the effect. As you can see from the following figure, the message is sent in the right chat box and the message is received in the left chat box.

web

Web side let’s optimize a few small problems:

  • The chat input box is empty after the message is sent
  • Use useRef to keep chat logs up to date

Feat – Multi-chat branch (feat-multi-chat branch

import React, { FC, useEffect, useState, useRef } from 'react';

const chatListRef = useRef<any[]>([]);

useEffect(() = > {
  chatListRef.current = chatList;
  setChatList(chatList);
}, [chatList]);

useEffect(() = > {
  socket.on('message'.(msg: any) = > {
    setChatList([...chatListRef.current, msg]);
    chatListRef.current = [...chatListRef.current, msg];
  });
}, [socket]);

const sendMessage = () = >{... setInputValue(' ');
};
Copy the code

Five, external functions

Mention a few small function points and optimization points, through the text description to achieve.

1. Loading state

Added a loading flag to the chatList object array.

Set sending to false, or set data loading to true if socket. On (‘message’,{}) listens for messages sent by the current ID.

And that’s going to simulate sending a message and whether it’s successful or not.

2, increaseenterListen for an event


// Keyboard binding events
const handleEnterKey = (e: any) = > {
  if (e.keyCode === 13) {
    // Call the method that sends the message}}; useEffect(() = > {
  document.addEventListener('keypress'.(e) = > handleEnterKey(e));
  return () = > document.removeEventListener('keypress'.(e) = >handleEnterKey(e)); } []);Copy the code

Project Git address

Writing is not easy, give a star ~