├── .gitignore ├── README.md ├── public ├── index.html └── client.js ├── client ├── index.html └── client.js ├── package.json ├── LICENSE ├── src ├── routes │ └── api.js └── utils.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | access.log 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # socket-message-push 2 | 3 | 一个利用 socket.io 进行消息推送的示例项目。 4 | 5 | 更多信息可见该博客:[利用 socket.io 实现消息实时推送](http://www.wukai.me/2017/08/27/push-message-with-socketio/) 6 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Socket.io Message Push Server 6 | 7 | 8 | 9 |

The answer to life, universe and everything.

10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script file should be added to client's html file 3 | */ 4 | 5 | // 此处路径应根据服务器进行设置 6 | const listenUrl = 'http://localhost:4001'; 7 | 8 | var socket = io(listenUrl); 9 | 10 | socket.on('receive_message', function (msg) { 11 | console.log('Here is message we got in client:', msg); 12 | }); -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Socket.io Message Push Test Cient 6 | 7 | 8 | 9 |

You know nothing.

10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket-message-push", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "dev": "cross-env NODE_ENV=development node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.17.2", 14 | "cross-env": "^5.0.1", 15 | "express": "^4.15.3", 16 | "morgan": "^1.8.2", 17 | "socket.io": "^2.0.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/client.js: -------------------------------------------------------------------------------- 1 | // 此处路径应根据服务器进行设置 2 | const listenUrl = 'http://localhost:4001'; 3 | 4 | const msgSocket = io(listenUrl, { 5 | 'reconnectionAttempts': 10, // 限制对于 socket 服务器的重连次数为10次 6 | }); 7 | 8 | const name = prompt("Please enter your name", "Jon Snow"); 9 | const tokenId = prompt("Please enter your tokenId", "1"); 10 | const userId = Math.floor(Math.random().toFixed(4) * 10000); 11 | 12 | msgSocket.on('connect', () => { 13 | msgSocket.emit('user_login', { 14 | userId, 15 | socketId: msgSocket.id, 16 | tokenId 17 | }); 18 | }); 19 | 20 | msgSocket.on('receive_message', function (msg) { 21 | console.log('Here is message we got in client:', msg); 22 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 wu kai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const { getSocketId } = require('./../utils'); 4 | 5 | router.use(function timeLog(req, res, next) { 6 | const m = new Date(); 7 | const dateString = `${m.getFullYear()}/${m.getMonth() + 8 | 1}/${m.getDate()} ${m.getHours()}:${m.getMinutes()}:${m.getSeconds()}`; 9 | console.log('Time: ', dateString); 10 | next(); 11 | }); 12 | 13 | router.post('/api', function(req, res) { 14 | if (process.env.NODE_ENV === 'development') { 15 | console.log("----------------- this is req's body -----------------"); 16 | console.log(req.body); 17 | console.log('------------------------------------------------------\n'); 18 | } 19 | 20 | const { tokens = [], entityType = '', data = {} } = req.body; 21 | tokens.forEach(tokenId => { 22 | const socketIds = getSocketId(users, tokenId); 23 | 24 | // 当后端post了一个消息时,通知client进行处理 25 | socketIds.forEach(function(socketId) { 26 | io.sockets.to(socketId).emit('receive_message', { 27 | entityType, 28 | data 29 | }); 30 | }); 31 | }); 32 | 33 | res.send({ 34 | responseCode: 000, 35 | data: 'OK!' 36 | }); 37 | }); 38 | 39 | module.exports = router; 40 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 建立了新的socket连接后,记录相应socketId 3 | */ 4 | function addSocketId(users, info) { 5 | const { tokenId, userId, socketId } = info; 6 | for (let i = 0; i < users.length; i++) { 7 | const user = users[i]; 8 | if (user.tokenId === tokenId) { 9 | if (user.socketIds.indexOf(socketId) === -1) { 10 | user.socketIds.push(socketId); 11 | } 12 | return; 13 | } 14 | } 15 | users.push({ 16 | userId, 17 | tokenId, 18 | socketIds: [socketId] 19 | }); 20 | } 21 | 22 | /** 23 | * 通过给定的tokenId获取socketId 24 | */ 25 | function getSocketId(users, tokenId) { 26 | // 同一用户会打开多个页面,存在多个socketId 27 | let result = []; 28 | users.forEach(user => { 29 | if (user.tokenId === tokenId) { 30 | result = user.socketIds; 31 | } 32 | }); 33 | return result; 34 | } 35 | 36 | /** 37 | * 关闭页面后,删除对应的socketId 38 | */ 39 | function deleteSocketId(users, socketId) { 40 | for (let i = 0; i < users.length; i++) { 41 | const user = users[i]; 42 | let sidIdx = user.socketIds.indexOf(socketId); 43 | if (sidIdx > -1) { 44 | user.socketIds.splice(sidIdx, 1); 45 | if (user.socketIds.length === 0) { 46 | users.splice(i, 1); 47 | break; 48 | } 49 | } 50 | } 51 | return; 52 | } 53 | 54 | module.exports = { 55 | getSocketId, 56 | addSocketId, 57 | deleteSocketId 58 | }; 59 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | const http = require('http').Server(app); 5 | const io = require('socket.io')(http); 6 | const bodyParser = require('body-parser'); 7 | const morgan = require('morgan'); 8 | const fs = require('fs'); 9 | 10 | const apiRoute = require('./src/routes/api'); 11 | 12 | const port = 4001; 13 | 14 | // io.set( 'origins', '*localhost:4000' ); 15 | io.origins('*:*'); 16 | 17 | const { addSocketId, getSocketId, deleteSocketId } = require('./src/utils'); 18 | 19 | app.use(express.static(path.join(__dirname, 'public'))); 20 | app.use(bodyParser.urlencoded({ extended: false })); 21 | app.use(bodyParser.json()); 22 | 23 | const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }); 24 | app.use(morgan('combined', { stream: accessLogStream })); 25 | 26 | global.users = []; // 记录下登录用户的tokenId, socketId 27 | global.io = io; 28 | 29 | app.get('/', function(req, res) { 30 | res.sendFile(__dirname + '/public/index.html'); 31 | }); 32 | 33 | app.get('/api', function(req, res) { 34 | res.send('.'); 35 | }); 36 | 37 | // 后端向 '/api' 路径post相应消息 38 | app.post('/api', apiRoute); 39 | 40 | io.on('connection', function(socket) { 41 | // console.log('a user connected'); 42 | // socket.broadcast.emit('new_user', {}); 43 | 44 | socket.on('disconnect', function() { 45 | console.log('user disconnected ', socket.id); 46 | deleteSocketId(users, socket.id); 47 | if (process.env.NODE_ENV === 'development') { 48 | displayUserInfo(); 49 | } 50 | }); 51 | 52 | socket.on('user_login', function(info) { 53 | const { tokenId, userId, socketId } = info; 54 | addSocketId(users, { tokenId, socketId, userId }); 55 | if (process.env.NODE_ENV === 'development') { 56 | displayUserInfo(); 57 | } 58 | }); 59 | }); 60 | 61 | http.listen(port, function() { 62 | console.log(`listening on port:${port}`); 63 | }); 64 | 65 | function displayUserInfo() { 66 | console.log('当前登录用户信息:'); 67 | console.log(users); 68 | } 69 | --------------------------------------------------------------------------------