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
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
- 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
- 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)
- 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) })
- 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
- 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 })
-
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 chatio.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) }); })
- 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 })
- 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 }) })
- 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 })
- 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:
- 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)
- Server received sendMsgGroup
socket.on('sendMsgGroup', (data) => { socket.to(data.roomId).emit('receiveMsgGroup', data); })
- 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
- 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 })
- 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.
- 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:
- The friends list is directly obtained from online users. You can apply for adding friends like group chat, and the other party agrees
- The user's group chat list is not stored. If you refresh the browser, you need to re create the group chat
- No browser notification for new messages
- ......
Reference resources
In short (YouChat) Thank you guys.