socket.io 旨在不同版本的浏览器间实现实时通信,在多种传输方式中自动选择最有效方式。
通信,双工
发送事件:socket.emit ('eventName',{data})
响应事件:socket.on( 'enventName' ,{data} )
//发送给所有的客户端包括自己
io.emit('hello', 'to all clients');
//发送给同一个房间的所有客户端包括自己
io.to('room42').emit('hello', "to all clients in 'room42' room");
io.on('connection', (socket) => {
//只发送给自己
socket.emit("hello","only sender");
//发送给所有客户端不包括自己
socket.broadcast.emit('hello', 'to all clients except sender');
//发送给同一个房间的所有客户端,不包括自己
socket.to('room42').emit('hello', "to all clients in 'room42' room except sender");
});
在以上的学习基础之上,建立了一个小的demo,来对socket.io有个深刻的理解。
需求:实现一个多人协同编辑一段文本
首先配置package.json
{
"name": "socket2",
"version": "1.0.0",
"description": "socket io demo",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"author": "daisy",
"license": "ISC",
"devDependencies": {},
"dependencies": {
"socket.io": "^2.0.4",
"socket.io-redis": "^5.2.0",
"node-windows": "^0.1.14"
}
}
执行命令安装相关的依赖:
npm install
接下来开始写服务端代码:建立一个js文件,名为app.js
const io = require('socket.io').listen(8124);
const redis = require('socket.io-redis');
const client = require('redis').createClient({ host: '192.168.103.52', port: 6379, detect_buffers: true });
const adapter = redis({ host: '192.168.103.52', port: 6379 });
const prefix = 'socket.io';
const namespace = 'mindLock';
adapter.pubClient.on('error', function () { });
adapter.subClient.on('error', function () { });
io.adapter(adapter);
function noop() { }
// 根据key获取redis信息
function getInfo(key, cb = noop) {
client.get(key, (err, reply) => {
let obj;
if (!reply) obj = {};
else obj = JSON.parse(reply.toString());
cb(obj);
});
}
io.on('connection', function (socket) {
let room = socket.handshake.query.id;
let userId = socket.handshake.query.userId;
if (!room) return socket.disconnect();
socket.join(room); // 加入房间
let channel = prefix + '#' + namespace + '#' + room + '#'; // redis中存储名
var value = getInfo(channel);
//初始化状态
socket.on("init", function () {
getInfo(channel, (obj) => {
if (!obj) { return; }
if (obj.isLock && obj.userId != userId) {
socket.emit("lock");
}
socket.emit("changeValue", { value: obj.inputValue });
})
});
socket.on('newValue', function (data) {
//发送内容,不包括自己
socket.broadcast.to(room).emit("lock");
socket.broadcast.to(room).emit("changeValue", { value: data.newValue });
//将最新的数据存到数据库
var obj = { inputValue: data.newValue, isLock: true, userId: userId };
client.set(channel, JSON.stringify(obj));
});
socket.on('unlock', function () {
//手动解锁,不包括自己
socket.to(room).emit("manuallyUnlock");
getInfo(channel, (obj) => {
obj["isLock"] = false;
client.set(channel, JSON.stringify(obj));
})
});
socket.on('disconnect', () => {
//断开连接,say goodbye ,包括自己
io.to(room).emit('leave', userId + ":left");
//如果是自己锁的,就解锁
getInfo(channel, (obj) => {
if (obj.userId == userId) {
obj["isLock"] = false;
socket.to(room).emit("manuallyUnlock");
client.set(channel, JSON.stringify(obj));
}
})
io.in(room).clients((err, clients) => {
if (!clients.length) client.del(channel);
});
});
});
io.of('/').adapter.clients((err, clients) => {
console.log(clients); // an array containing all connected socket ids
});
然后再编写客户端代码
新建一个html文件,名为index.html,
代码如下:
socket.io 测试
{{title}}
锁
解锁
这样一切就准备就绪,然后启动服务器,使用命令 node app.js 。
用两个浏览器打开index.html,可以看到如下的效果,同时只有一个人可以锁定编辑,锁定编辑的人可以解锁。一旦开始编辑,就上锁。编辑内容实时同步。
以上就是demo的全部代码,希望能够帮助大家学习socket.io的执行原理。
下一篇文章介绍如何将nodejs服务安装成windows服务,使得开机启动。