socket.io——学习demo

socket.io 旨在不同版本的浏览器间实现实时通信,在多种传输方式中自动选择最有效方式。

通信,双工
发送事件:socket.emit ('eventName',{data})
响应事件:socket.on( 'enventName' ,{data} )

socket.io 服务端API总结:

//发送给所有的客户端包括自己
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有个深刻的理解。

需求:实现一个多人协同编辑一段文本

服务器端采用nodejs+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,

客户端使用vue+element-ui

代码如下:

<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>socket.io 测试</title>
    <script src="./script/socket.io.js"></script>
    <script src="./script/vue.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>

</head>

<body>
    <div id="app">
        <div id="title">
            <p>{{title}}</p>
        </div>
        <el-input id="input" type="textarea" :rows="6" style="width: 50%" placeholder="请输入内容" v-model="inputText" v-bind:readonly="isLock">
        </el-input>
        <el-button v-bind:type="lockType" icon="el-icon-goods" circle>锁</el-button>
        <el-button type="primary" round v-if="!isLock" v-on:click="clickLock">解锁</el-button>
    </div>
</body>

</html>

<script>
    var getParam = function (name) {
        var search = document.location.search;
        var pattern = new RegExp("[?&]" + name + "\=([^&]+)", "g");
        var matcher = pattern.exec(search);
        var items = null;
        if (null != matcher) {
            try {
                items = decodeURIComponent(decodeURIComponent(matcher[1]));
            } catch (e) {
                try {
                    items = decodeURIComponent(matcher[1]);
                } catch (e) {
                    items = matcher[1];
                }
            }
        }
        return items;
    };
    function randomId() {
        var tmp = "";
        var str = "0123456789qwertyuioplkjhgfdsazxcvbnm";
        var length = str.length;
        var timestamp = new Date().getTime();
        for (var i = 0; i < 5; i++) {
            tmp += str.charAt(Math.floor(Math.random() * length))
        }
        tmp += timestamp;
        return tmp;
    };
    window.id = this.getParam("id");
    window.userId = randomId();

    var socket = io.connect('http://localhost:8124', {
        query: {
            id: window.id,
            userId: window.userId
        }
    });

    var vm = new Vue({
        el: "#app",
        data: {
            title: "",
            inputText: "",
            isLock: false,
            socket: socket
        },
        computed: {
            lockType: function () {
                return this.isLock ? 'danger' : 'success';
            }
        },
        methods: {
            clickLock: function () {
                if (!this.isLock) {
                    this.socket.emit('unlock');
                }
            }
            // changeInput: function (data) {
            //     this.inputText = data;
            // },
            // changeTitle: function (data) {
            //     this.title = data;
            // }
        },
        watch: {
            inputText: function () {
                if (!this.isLock && typeof (this.inputText) != "undefined") {
                    debugger;
                    this.socket.emit('newValue', { newValue: this.inputText });
                }
                // else {
                //     this.isLock = false;
                // }
            }
        }
    });

    socket.emit('init');

    socket.on('changeValue', function (data) {
        vm.inputText = data.value;
    });

    socket.on('lock', function (data) {
        vm.isLock = true;
    });

    socket.on('leave', function (data) {
        vm.$message(data);
    });

    socket.on('manuallyUnlock', function (data) {
        vm.isLock = false;
    });

</script>

这样一切就准备就绪,然后启动服务器,使用命令 node app.js 。

demo展示结果

用两个浏览器打开index.html,可以看到如下的效果,同时只有一个人可以锁定编辑,锁定编辑的人可以解锁。一旦开始编辑,就上锁。编辑内容实时同步。


以上就是demo的全部代码,希望能够帮助大家学习socket.io的执行原理。

下一篇文章介绍如何将nodejs服务安装成windows服务,使得开机启动。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值