• node.js中使用socket.io + express进行实时消息推送


    socket.io是一个websocket库,包含客户端的js和服务端的node.js,可以在不同浏览器和移动设备上构建实时应用。

    一、安装 socket.io

    1

    npm install socket.io

      

    二、通过socket.io创建一个简单应用

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    const http = require('http');

    const path = require('path');

    const express = require('express');

    //创建一个应用,注意app其实就是一个函数,类似function(req, res) {}

    let app = express();

    //创建一个http服务器,既然app是一个函数,那这里就可以传入。

    let server = http.createServer(app);

    //注意,websocket的握手是需要依赖http服务的,所以这里要把server传入进去。

    let io = require('socket.io')(server);

    app.get('/'function (req, res) {

        res.sendFile(path.join(__dirname, 'index.html'));

    });

    //有新的客户端连接时触发

    io.on('connection'function (socket) {

        //接收到消息时触发

        socket.on('message'function (data) {

            console.log('服务端收到 : ', data);

            //注意send()方法其实是发送一个 'message' 事件

            //客户端要通过on('message')来响应

            socket.send('你好客户端, ' + data);

        });

        //发生错误时触发

        socket.on('error'function (err) {

            console.log(err);

        });

    });

    server.listen(8888);

    index.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    <!DOCTYPE html>

    <html lang="zh-CN">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="send" value="发送">

    <ul id="receive"></ul>

    <!-- /socket.io/socket.io.js 这个引用路径是固定的,socket.io会自动帮我们解析 -->

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888');

        //连接成功时触发

        socket.on('connect'function () {

            console.log('连接成功');

        });

        //连接断开时触发

        socket.on('disconnect'function () {

            console.log('连接断开');

        });

        //收到消息时触发

        socket.on('message'function (data) {

            var node = document.createElement("li");

            node.innerHTML = "客户端收到 : " + data;

            document.querySelector("#receive").appendChild(node);

        });

        document.querySelector("#send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            socket.send(msg);

        };

    </script>

    </body>

    </html>

    这样我们就可以在客户端建立与服务端的实时消息传送。注意 send() 方法只是 emit 方法的封装,等同于 emit('message', args)。

    我们通过 emit 方法可以自定义的发送事件,并监听事件。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    const http = require('http');

    const path = require('path');

    let app = require('express')();

    let server = http.createServer(app);

    let io = require('socket.io')(server);

    app.get('/'function (req, res) {

        res.sendFile(path.join(__dirname, 'index.html'));

    });

    io.on('connection'function (socket) {

        socket.on('message'function (data) {

            console.log('服务端收到 : ', data);

            socket.send('你好客户端, ' + data);

        });

        //监听自定义事件

        socket.on('myevent'function (data) {

            console.log('客户端发送了一个自定义事件', data);

        });

    });

    server.listen(8888);

    index.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    <!DOCTYPE html>

    <html lang="zh-CN">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="send" value="发送">

    <input type="button" id="event" value="发送自定义事件">

    <ul id="receive"></ul>

    <!-- /socket.io/socket.io.js 这个引用路径是固定的,socket.io会自动帮我们解析 -->

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888');

        //连接成功时触发

        socket.on('connect'function () {

            console.log('连接成功');

        });

        //连接断开时触发

        socket.on('disconnect'function () {

            console.log('连接断开');

        });

        //收到消息时触发

        socket.on('message'function (data) {

            var node = document.createElement("li");

            node.innerHTML = "客户端收到 : " + data;

            document.querySelector("#receive").appendChild(node);

        });

        document.querySelector("#send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            socket.send(msg);

        };

        document.querySelector("#event").onclick = function () {

            var msg = document.querySelector("#msg").value;

            //参数一表示,事件的名称

            //参数二表示,要发送的数据

            socket.emit("myevent", msg);

        };

    </script>

    </body>

    </html>

    emit() 或 send() 还有第三个参数,用来设置消息发送成功后的回执。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    const http = require('http');

    const path = require('path');

    const express = require('express');

    let app = express();

    let server = http.createServer(app);

    let io = require('socket.io')(server);

    app.use(express.static(path.join(__dirname)));

    app.get('/'function (req, res) {

        res.sendFile(path.join(__dirname, 'index.html'));

    });

    io.on('connection'function (socket) {

        socket.on('message'function (data, callback) {

            socket.send('服务器发送 : ' + data, function (data) {

                console.log(data);

            });

            //这里callback传入的参数会传递到客户端的send()回调函数里

            callback('服务端的回执');

        });

    });

    server.listen(8888);

    index.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="send" value="发送">

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888');

        socket.on('connect'function () {

            console.log('连接成功');

        });

        socket.on('message'function (data, callback) {

            console.log('客户端收到 : ', data);

            //这里callback传入的值会传递到服务端的send()回调函数里

            callback('客户端的回执');

        });

        document.querySelector("#send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            socket.send(msg, function (data) {

                console.log(data);

            });

        };

    </script>

    </body>

    </html>

      

    三、socket.io命名空间的概念

    有些时候我们需要按不同的模块或功能去传递不同的消息,比如在 /user 模块下推送用户信息,在 /order 模块下推送订单信息,两者间互不干扰。

    这个时候就需要用到命名空间了,socket.io把不同命名空间下的消息和事件分隔开了。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    const http = require('http');

    const path = require('path');

    const express = require('express');

    let app = express();

    let server = http.createServer(app);

    let io = require('socket.io')(server);

    app.use(express.static(path.join(__dirname)));

    app.get('/'function (req, res) {

        res.sendFile(path.join(__dirname, 'index.html'));

    });

    //通过of()设置命名空间

    //注意,如果没加of(),则默认使用'/'命名空间

    io.of('/user').on('connection'function (socket) {

        socket.on('message'function (data) {

            console.log('/user : ', data);

            //注意send()只会发送给当前客户端

            //如果要进行群发

            //用 io.of(命名空间).send() 发送命名空间下所有客户端,包括发送者。

            //或者 socket.broadcast.send() 发送命名空间下所有客户端,不包括发送者。

            //io.of('/user').send('服务端发送 : ' + data);

            socket.broadcast.send('服务端发送 : ' + data);

        });

    });

    io.of('/order').on('connection'function (socket) {

        socket.on('message'function (data) {

            console.log('/order : ', data);

            socket.broadcast.send('服务端发送 : ' + data);

        });

    });

    server.listen(8888);

    user.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="send" value="发送">

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888/user');

        socket.on('connect'function () {

            console.log('连接成功');

        });

        socket.on('message'function (data) {

            console.log('客户端收到 : ', data);

        });

        document.querySelector("#send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            //注意客户端的send()会发送到当前的socket命名空间下

            socket.send(msg);

        };

    </script>

    </body>

    </html>

    order.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="send" value="发送">

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888/order');

        socket.on('connect'function () {

            console.log('连接成功');

        });

        socket.on('message'function (data) {

            console.log('客户端收到 : ', data);

        });

        document.querySelector("#send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            //注意客户端的send()会发送到当前的socket命名空间下

            //socket.send(msg);

            socket.send(msg);

        };

    </script>

    </body>

    </html>

    /user 和 /order 不同命名空间下的消息彼此之间无法看到。

    四、socket.io房间的概念

    房间是一个命名空间下划分的,一个客户端可以进入多个房间。

    如果在命名空间下进行广播,那该命名空间下的所有客户端和房间内的客户端都会收到消息。

    如果在房间内进行广播,则该房间下的所有客户端会收到消息,房间外的不会影响。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    const http = require('http');

    const path = require('path');

    const express = require('express');

    let app = express();

    let server = http.createServer(app);

    let io = require('socket.io')(server);

    app.use(express.static(path.join(__dirname)));

    app.get('/'function (req, res) {

        res.sendFile(path.join(__dirname, 'index.html'));

    });

    io.of('/user').on('connection'function (socket) {

        let rooms = [];

        //加入房间

        socket.on('join'function (name) {

            socket.join(name, function () {

                if (!rooms.includes(name)) {

                    rooms.unshift(name);

                }

                console.log(`${socket.id} 加入房间 ${name}`);

                console.log(rooms);

            });

        });

        //离开房间

        socket.on('leave'function (name) {

            socket.leave(name, function () {

                rooms = rooms.filter(function (value) {

                    return value !== name;

                });

                console.log(`${socket.id} 离开房间 ${name}`);

                console.log(rooms);

            });

        });

        //房间内的广播

        socket.on('room_broadcast'function (data) {

            //socket.to(rooms[0]).send('房间 ${rooms[0]} 内的广播 : ' + data); 房间下的所有客户端,不包括发送者

            //io.of(命名空间).in(rooms[0]).send(`房间 ${rooms[0]} 内的广播 : ${data}`); 房间下的所有客户端,包括发送者

            io.of('/user').in(rooms[0]).send(`房间 ${rooms[0]} 内的广播 : ${data}`);

        });

        //命名空间下的广播

        socket.on('namespace_broadcast'function (data) {

            //socket.broadcast.send('命名空间下的广播 : ' + data); 命名空间下所有客户端,不包括发送者

            //io.of(命名空间).send('命名空间下的广播 : ' + data); 命名空间下所有客户端,包括发送者

            io.of('/user').send('命名空间下的广播 : ' + data);

        });

    });

    io.of('/order').on('connection'function (socket) {

        socket.on('message'function (data) {

            io.of('/order').send('命名空间下的广播 : ' + data);

        });

    });

    server.listen(8888);

    user.html的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>Title</title>

    </head>

    <body>

    <input type="text" id="msg">

    <input type="button" id="room_send" value="房间内的广播">

    <input type="button" id="namespace_send" value="命名空间下的广播">

    <input type="button" class="join" room="001" value="加入房间001">

    <input type="button" class="join" room="002" value="加入房间002">

    <input type="button" class="leave" room="001" value="离开房间001">

    <input type="button" class="leave" room="002" value="离开房间002">

    <script src="/socket.io/socket.io.js"></script>

    <script>

        var socket = io.connect('http://localhost:8888/user');

        socket.on('connect'function () {

            console.log('连接成功');

        });

        socket.on('message'function (data) {

            console.log('客户端收到 : ', data);

        });

        document.querySelector("#room_send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            socket.emit("room_broadcast", msg);

        };

        document.querySelector("#namespace_send").onclick = function () {

            var msg = document.querySelector("#msg").value;

            socket.emit("namespace_broadcast", msg);

        };

        var joins = document.querySelectorAll(".join");

        for (var ix = 0; ix < joins.length; ix++) {

            joins[ix].onclick = function () {

                socket.emit('join', this.getAttribute("room"));

            };

        }

        var leaves = document.querySelectorAll(".leave");

        for (var ix = 0; ix < leaves.length; ix++) {

            leaves[ix].onclick = function () {

                socket.emit('leave', this.getAttribute("room"));

            };

        }

    </script>

    </body>

    </html>

    通过 join() 和 leave() 加入房间或离开房间。通过 to() 方法指定向哪个房间发送消息。要发送多个房间,可以调用多次 to()。

  • 相关阅读:
    数据结构与算法系列——排序(6)_树形选择排序
    数据结构与算法系列——排序(7)_堆排序
    数据结构与算法系列——排序(5)_简单选择排序
    数据结构与算法系列——排序(4)_Shell希尔排序
    数据结构与算法系列——排序(3)_折半插入排序
    数据结构与算法系列——排序(2)_直接插入排序
    数据结构与算法系列——排序(1)_概述
    Java高级开发_性能优化的细节
    图形推理
    美团点评面试20190515
  • 原文地址:https://www.cnblogs.com/wjlbk/p/12884680.html
Copyright © 2020-2023  润新知