node.js中使用socket.io + express進行實時消息推送


socket.io是一個websocket庫,包含客戶端的js和服務端的node.js,可以在不同瀏覽器和移動設備上構建實時應用。

一、安裝 socket.io

npm install socket.io

  

二、通過socket.io創建一個簡單應用

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的代碼:

<!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 方法可以自定義的發送事件,並監聽事件。

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的代碼:

<!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() 還有第三個參數,用來設置消息發送成功后的回執。

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的代碼:

<!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把不同命名空間下的消息和事件分隔開了。

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的代碼:

<!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的代碼:

<!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房間的概念

房間是一個命名空間下划分的,一個客戶端可以進入多個房間。

如果在命名空間下進行廣播,那該命名空間下的所有客戶端和房間內的客戶端都會收到消息。

如果在房間內進行廣播,則該房間下的所有客戶端會收到消息,房間外的不會影響。

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的代碼:

<!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()。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM