socket.io

Socket.IO is an open source framework implemented entirely by JavaScript, based on Node.js, and supporting WebSocket protocols for real-time communication, cross-platform. It includes JavaScript on the client side and Node.js on the server side.

The service side

Basic use based on KOA and socket. IO

const koa = require('koa')
const app = new koa()
const server = require('http').createServer(app.callback())
const io = require('socket.io')(server) // Listen for the connect event IO. On ('connection', socket => {
  socket.emit('open'); // Notify the client that console.log('connected'); // Listen to the disconnect event socket.on('disconnect', () => {
    console.log('disconnect')}}); server.listen(3001);Copy the code

The client

import io from 'socket.io-client'; // establish webSocket connection const socket = IO ('http://127.0.0.1:3001'); // Receive server connection acknowledgement socket.on('open', () => {
    showTip('socket io is open ! ');
    init();
});
Copy the code

socket.io

practice

Blog address

Test account: admin

Test password: admin

Please click on the “Testing socket. IO functionality” detail comment article to see how it works

The service side

1. Write the tool function startWebSocketApp and webSocketObj instance export

const roomPrefix = 'user_'; const webSocketObj = { noticeRooms: {}, webSocketIo: Null, // Send a message sendNotice(userId, data) {const {noticeRooms, webSocketIo} = this; const keys = Object.keys(noticeRooms);if(! userId || ! webSocketIo || ! keys.length || ! data) {return} const sockets = webSocketIo.sockets.sockets; Room const currentSocket = sockets[noticeRooms['${roomPrefix}${userId}`]].if(currentSocket) {// emit notification currentsocket.emit (currentSocket)'getNotice', data); }}, // Send the global system message sendSystemNotice(data) {const {noticeRooms, webSocketIo} = this; const keys = Object.keys(noticeRooms);if(! data || ! webSocketIo || ! keys.length) {return} const sockets = this.webSocketIo.sockets.sockets; // keys = keys.filter(key => noticeRooms[key] ! == socket.id); keys.forEach(key => { const currentSocket = sockets[noticeRooms[key]];if (currentSocket) {
				currentSocket.emit('getNotice', data); }}); }}; const startWebSocketApp = server => { const webSocketIo = webSocketObj.webSocketIo = require('socket.io')(server, {path: '/notice'}); const {noticeRooms} = webSocketObj; //webSocket listening method webSocketio.on ('connection', socket => {
		console.log('Initialization successful! Now we can bind and trigger events using sockets.);
		socket.on(`joinNoticeRoom`, data => {
			console.log('Join the room:', data); // Record the current room ID noticeRooms[' user_] based on the current user ID${data}`] = socket.id;
		});
	});
};

module.exports = {startWebSocketApp, webSocketObj};
Copy the code

2. Global application tool function startWebSocketApp

const {startWebSocketApp} = require('./server/utils/web-socket');
const app = new Koa();
const server = require('http').Server(app.callback()); // Start webSocket startWebSocketApp(server); server.listen(config.port, () => { console.log(`starting at port:${config.port}`);
});
Copy the code

3. Database table design

  • Table of Sending and receiving relationships (Message)
CREATE TABLE  IF NOT EXISTS `message` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sendId` int(11) DEFAULT NULL COMMENT 'Sender ID',
  `recId` int(11) DEFAULT NULL COMMENT 'Recipient ID (all users when recId is 0)',
  `messageId` int(11) DEFAULT NULL COMMENT 'the message content id',
  `createDate` varchar(50) DEFAULT NULL COMMENT 'Send date',
   PRIMARY KEY (`id`)
)  ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code
  • Send message table (message_content)
CREATE TABLE  IF NOT EXISTS `message_content` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` int(11) NULL DEFAULT NULL COMMENT '10: Article comment 20: Article Comment Reply (Business scalable) ',
  `title` varchar(255) DEFAULT NULL COMMENT 'Comment Subject',
  `sourceId` int(11) NULL DEFAULT NULL COMMENT 'Comment source ID',
  `content` longtext DEFAULT NULL COMMENT 'content',
  `createDate` varchar(50) DEFAULT NULL COMMENT 'Send date',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code
  • User message relationship table (message_user)
CREATE TABLE  IF NOT EXISTS `message_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) DEFAULT NULL COMMENT 'user id',
  `messageId` int(11) DEFAULT NULL COMMENT 'information id',
  `status` int(11) NULL DEFAULT 10 COMMENT '(10: read)',
  `createDate` varchar(50) DEFAULT NULL COMMENT 'Reading Date',
   PRIMARY KEY (`id`)
)  ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code

4. Specific use

  • Each time a message is sent, insert a piece of data into the message_Content table to get the message_ID.
  • The message table records fields such as sender, receiver, and message_ID. (If recId is 0, the message notification is sent to all users, avoiding the impact of inserting n pieces of data at a time.)
  • After reading, the user inserts a piece of data into the message_user table to indicate that he or she has read. (If he or she has read, he or she must join the Message_user table instead of stuffing all the data in.)

5. Create a notice SQL statement method

const notice = {
	async createMessageContent(data) {
		const sqlStatement = `insert into ${MESSAGE_CONTENT} (type, title, content, sourceId, createDate) values (? ,? ,? ,? ,?) `; const {type, title, content, sourceId} = data;
		const currentDate = formatDate(new Date());
		return query(sqlStatement, [type, title, content, sourceId, currentDate])
	},
	async createMessage(data) {
		const sqlStatement = `insert into ${MESSAGE}(sendId, recId, messageId, createDate) values (? ,? ,? ,?) `; const {sendId, recId, messageId} = data; const currentDate = formatDate(new Date());return query(sqlStatement, [sendId, recId, messageId, currentDate])
	},
	async createMessageUser(data) {
		const sqlStatement = `insert into ${MESSAGE_USER}(userId, messageId, createDate) values (? ,? ,?) `; const {userId, messageId} = data; const currentDate = formatDate(new Date());return query(sqlStatement, [userId, messageId, currentDate])
	},
	async batchCreateMessageUser(values) {
		const sqlStatement = `insert into ${MESSAGE_USER}(userId, messageId, createDate) values ? `;return query(sqlStatement, [values])
	},
	async getUnreadMessageList(id) {
		const sqlStatement = `
		SELECT
			NOTICE_CONTENT.*,
			USER.profilePicture,
			USER.username sendName
		FROM
			(
			SELECT
				MESSAGE_CONTENT.*,
				MESSAGE_USER.STATUS 
			FROM
				(
				SELECT
					MESSAGE.sendId,
					MESSAGE.recId,
					MESSAGE.messageId,
					CONTENT.*
				FROM
					( SELECT * FROM message WHERE recId = ${id} ) MESSAGE
					LEFT JOIN message_content CONTENT ON MESSAGE.messageId = CONTENT.id 
				) MESSAGE_CONTENT
				LEFT JOIN message_user MESSAGE_USER ON MESSAGE_CONTENT.messageId = MESSAGE_USER.messageId 
			WHERE
			STATUS IS NULL 
	) NOTICE_CONTENT
	LEFT JOIN user_info USER ON NOTICE_CONTENT.sendId = USER.id
	`;
		returnquery(sqlStatement); }}; module.exports = notice;Copy the code

6. Create a notice Controller method

const notice = {
	async createNotice(data) {
		const {sendId, recId, content, title, type.sourceId} = data;
		const messageContentRes = await noticeSql.createMessageContent({content, title, type.sourceId});
		if (messageContentRes && messageContentRes.insertId) {
			const messageId = messageContentRes.insertId;
			const messageRes = await noticeSql.createMessage({sendId, recId, messageId});
			if (messageRes && messageRes.insertId) {
				const userList = await userSql.getAllUserList();
				if (userList && userList.length) {
					const sendName = userList.find(user => user.id === sendId).username;
					const noticeResult = {sendName, content, title, type};
					if (recId === 0) {
						webSocketObj.sendSystemNotice(noticeResult)
					} else {
						webSocketObj.sendNotice(recId, noticeResult)
					}
				} else{console.log(' user table does not exist or user does not exist, Cannot send notification ')}}}}, async getUnreadMessageList(CTX) {const authorization = ctX.header.authorization; const userInfo = await getTokenResult(authorization); const {id} = userInfo; const response = createResponse(); const unreadMessageList = await noticeSql.getUnreadMessageList(id);if (unreadMessageList && unreadMessageList.length) {
			response.code = 0;
			response.message = 'success';
			response.results = unreadMessageList;
		}else {
			response.code = 404;
			response.message = 'Information does not exist';
		}
		ctx.body = response;
	},
	async createMessageUser(ctx) {
		const authorization = ctx.header.authorization;
		const userInfo = await getTokenResult(authorization);
		const requestBody = ctx.request.body;
		const {id} = userInfo;
		const {messageId} = requestBody;
		const response = createResponse();
		const res = await noticeSql.createMessageUser({messageId, userId: id});
		if(res && res.insertId ! == undefined) { response.message ='success';
		}
		ctx.body = response;
	},
	async batchCreateMessageUser(ctx) {
		const authorization = ctx.header.authorization;
		const userInfo = await getTokenResult(authorization);
		const requestBody = ctx.request.body;
		const {id} = userInfo;
		const {messageIdList} = requestBody;
		const currentDate = formatDate(new Date());
		const sqlValues = messageIdList.map(messageId => [id, messageId, currentDate]);
		const response = createResponse();
		const res = await noticeSql.batchCreateMessageUser(sqlValues);
		if(res && res.insertId ! == undefined) { response.message ='success'; } ctx.body = response; }}; module.exports = notice;Copy the code

7. Add a Notice server route

const Router = require('koa-router');
const noticeModel = require('.. /controller/notice');
const notice = new Router();

notice
	.get('/message-un-read', async ctx => noticeModel.getUnreadMessageList(ctx))
	.post('/message-read', async ctx => noticeModel.createMessageUser(ctx))
	.post('/message-read-batch', async ctx => noticeModel.batchCreateMessageUser(ctx))
;
module.exports = notice;
Copy the code

The client

1. Download the socket. IO – client

yarn add socket.io-client -S
Copy the code

2. Create the web-socket.js tool method

import io from 'socket.io-client';


export default class WebSocket {
	socket = null;

	connect(url, path = '/socket.io') {
		console.log('Connect socket');
		this.socket = io(url, {path, reconnection: true, reconnectionDelay: 10000});
	}

	disconnect() {
		if(this.socket ! = null){ console.log('Disconnect socket');
			this.socket.disconnect();
			this.socket.close();
			this.socket = null;
		}
	}

	register(channel, listener){
		if(this.socket ! = null){ this.socket.on(channel, listener); }}};Copy the code

3. The blog system connects to the socket. IO service

import {noticeTypeList} from ".. /.. /.. /.. /conf";
import WebSocket from ".. /.. /.. /.. /lib/plugins/web-socket";

startWebSocketServer(userId) {
		const webSocket = new WebSocket();
		const {NODE_ENV, SOCKET_URL} = process.env;
		webSocket.connect(`${SOCKET_URL}`, `${NODE_ENV === 'production' ? '/prod' : ''}/notice`);

		webSocket.register('connect', ()=>{
			console.log('Socket connected');
			webSocket.socket.emit('joinNoticeRoom', userId);
			WebLayout.createWebSocket(userId, webSocket);
		});

		webSocket.register('disconnect', ()=>{
			console.log('Socket disconnected');
		});

		webSocket.register('error', (msg)=>{
			console.log(msg);
		});

		webSocket.register('getNotice', data => {
			const {sendName, content, title, type} = data;
			this.props.getUnreadMessageList();
			notification.open({
				message: `${sendName}${noticeTypeList[type]}"${title}"`,
				description: content,
			});
		});
	}
	
	componentDidMount() {
		const {userInfo: {userId}} = this.props;
		const token = localStorage.getItem('authorization');
		const isLogin = userId && token;
		isLogin && this.startWebSocketServer(userId);
	}

	componentWillReceiveProps = nextProps => {
		const {userInfo: {userId}} = this.props;
		const {userId: currentUserId} = nextProps.userInfo;
		if(userId ! == currentUserId) {if (currentUserId) {
				localStorage.getItem('authorization') && this.startWebSocketServer(currentUserId); }}};Copy the code

4. Front end effect

Special note: NGINX supports WebSocket by allowing tunneling between clients and back-end servers. In order for NGINX to send Upgrade requests from clients to back-end servers, the Upgrade and Connection headers must be explicitly set, as shown in the following example:

location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}
Copy the code

www.nginx.com/blog/websoc…