番外:关门请上锁——“socket.io”嵌套监听
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

呵呵,自从在 5 级的段位被一个砍给绊倒之后,就再也没有爬起来过,一直在现实与虚幻之间原地踏步,清闲之余,也做了几篇番外,番外篇都没有什么水准,只是自己平时遇到问题,然后做了个小总结而已,大神们感觉低端的话,千万不要来喷我啊。

如果对 node.js 以及 socket.io 恐惧的话,请移步吧。要是感兴趣,稍微看一下入门,就可以来看我扯蛋了。

基础复习

socket.io 中,客户端可以通过 io.connect('http://127.0.0.1:1314/bolt') 连接一个名为 bolt 的命名空间,在服务端要通过 io.of('/bolt') 来与之对应,这个是基础哈,代码如下:

  • 服务端

      var io = require('socket.io').listen(1314);
    
      io.of('/bolt').on('connection', function (socket) {
        socket.emit('news', { hello: 'world' });
        socket.on('my other event', function (data) {
          console.log(data);
        });
      });
    
  • 客户端

    var io = require('socket.io-client');
    var socket = io.connect('http://127.0.0.1:1314/bolt');
    
    socket.on('news', function (data) {
      console.log(data);
      socket.emit('my other event', { my: data.hello });
    });
    

这时,服务端会输出一个{ my: ‘world’ },客户端会输出一个 { hello: 'world' },与预想相同。

提出问题

如果,我想在客户端连接时,创建一个名为 bolt.b 的命名空间呢?

  • 服务端

      var io = require('socket.io').listen(1314);
    
      io.of('/bolt').on('connection', function (socket) {
        socket.emit('news', { hello: 'world' });
        socket.on('my other event', function (data) {
          console.log(data);
        });
    
        io.of('/bolt.b').on('connection', function (socket) {
          socket.emit('news', { hello: 'world' });
          socket.on('my other event', function (data) {
            console.log(data);
          });
        });
      });
    
  • 客户端

    var io = require('socket.io-client');
    var socket = io.connect('http://127.0.0.1:1314/bolt');
    
    socket.on('news', function (data) {
      console.log(data);
      socket.emit('my other event', { my: data.hello });
    
      var b = io.connect('http://127.0.0.1:1314/bolt');
    
      b.on('news', function (data) {
        console.log(data);
        socket.emit('my other event', { my: 'b' });
      });
    });
    

这时,服务端会输出一个{ my: ‘world’ } { my: ‘b’ },客户端会输出两个 { hello: 'world' },与预想相同,但是不要高兴太早,当第二个客户端连接的时候,就出现问题了。

服务端输出了一个{ my: ‘world’ } { my: ‘b’ } { my: ‘b’ },客户端输出了三个 { hello: 'world' },发生了什么情况呢?

作出假设

当第二个客户端连接时,服务端会再一次创建一个 bolt.bconnection 事件,而在第二个客户端中连接 bolt.b 时,相当于执行了这样一个回调函数:

          socket.emit('news', { hello: 'world' });
          socket.on('my other event', function (data) {
            console.log(data);
          }
          socket.emit('news', { hello: 'world' });
          socket.on('my other event', function (data) {
            console.log(data);
          }

你他妈在逗我?不知道你有没有看出来,服务端向客户端发送了两次 { hello: 'world' },客户端就会回应两次 { hello: 'b' },而每一次服务端有两次 my other event 时间,应该是四次啊亲,难道,他俩就不合并了?

初步解决

额,先不管服务端到底应该输出两次还是四次 { hello: 'b' } 了。

对于重复监听连接时间,小弟不才提出了这个 关门加锁 的解决方案:

  • 服务端

      var io = require('socket.io').listen(1314);
      var lock = {};
    
      io.of('/bolt').on('connection', function (socket) {
        socket.emit('news', { hello: 'world' });
        socket.on('my other event', function (data) {
          console.log(data);
        });
    
        if (!lock['b']) {
          io.of('/bolt.b').on('connection', function (socket) {
            socket.emit('news', { hello: 'world' });
            socket.on('my other event', function (data) {
              console.log(data);
            });
          });
          lock['b'] = true;
        };
      });
    

嗯,就是引入一个 lock 对象,一旦添加了这个事件监听,就上一把锁,很好理解吧。

更多问题

一旦服务端重启,客户端在接收到 news 信息事件时,也会重复创建连接事件,也必须使用相同的加锁方式:

  • 客户端

    var io = require('socket.io-client');
    var socket = io.connect('http://127.0.0.1:1314/bolt');
    
    var lock = {};
    
    socket.on('news', function (data) {
      console.log(data);
      socket.emit('my other event', { my: data.hello });
    
      var b = io.connect('http://127.0.0.1:1314/bolt');
    
      if (!lock['b']) {
        b.on('news', function (data) {
          console.log(data);
          socket.emit('my other event', { my: 'b' });
        });
        lock['b'] = true;
      }
    });
    

当我改变 bolt.b 的服务端 my other event 时,我发现服务端竟然还能输出 { my: ‘b’ } ,原来是客户端的回应对象把 b.emit('my other event', { my: 'b' }); 写成了 socket.emit('my other event', { my: 'b' });。额,这就解决了上面

服务端到底应该输出两次还是四次 { hello: 'b' }

的问题了。

其实这篇文章我是想说,一个良好的代码命名习惯是多么的重要!!!

评论
发表评论
5年前

其实Kata3、Kata4的题都不算那么难呐…

WRITTEN BY
BoltDoggy
A doggy named Bolt.
TA的新浪微博
PUBLISHED IN
My Code Wars 我的代码战争【休息一下】

这里讲述的是一个8级(kyu)菜鸟立志成为8段(dan)大神的故事。

他在代码战争中一次次遭遇挫败,而又在乱炖中获得高人指点。

遥想当年自高自大,而今终知天外有天。

坚持要走出一条自己的路,这是一条不归路。


Bolt 在写underscore.js源码分析专栏的时候发现了一个问题,就是看别人写的代码的时候自认为看懂了,但实际上有理解错误自己也不知道。

另外就是看了再牛逼的代码,自己写的时候,还是那些小套套。

于是 Bolt 开了一个新专栏:My Code Wars 我的代码战争。在同一需求下,菜鸟写的代码与大神有什么区别,是思维方式不同,还是什么原因导致的?

欢迎大家来新专栏提供意见和建议。

我的收藏