├── .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 |
--------------------------------------------------------------------------------