socket.io学习教程之深入学习篇(三)


Posted in Javascript onApril 29, 2017

前言

socket.io提供了基于事件的实时双向通讯,本文深入的介绍了socket.io,下面来看看详细的内容吧。

静态文件

socket.io默认情况下会通过socket.io-client包提供socket.io.min.js和socket.io.js.map下载

运行实例app.js

let app = require('http').createServer() 
let io = require('socket.io')(app)

app.listen(3000);

浏览器访问http://localhost:3000/socket.io/socket.io.js可以加载压缩的源码,访问http://localhost:3000/socket.io/socket.io.js.map加载sourcemap

我们可以改变这种行为

禁用socket.io.js下载

方法1: 实例化时传入控制参数serveClient值false

let io = require('socket.io')(app, { 
 serveClient: false
})

方法2: 调用函数serverClient

let app = require('http').createServer() 
let io = require('socket.io')() 
io.serveClient(false) 
io.listen(app) // 或者io.attach(app)

如果在调用函数前服务已绑定http.Server,该方法将不起作用

禁用后再次访问将提示{"code":0,"message":"Transport unknown"}

修改静态文件路径

socket.io.js路径可以改变,其默认路径为/socket.io。

实例化时传参

let io = require('socket.io')(app, { 
 path: '/io'
})

调用函数path

let app = require('http').createServer() 
let io = require('socket.io')() 
io.path('/io') 
io.listen(app)

如果在调用函数前服务已绑定http.Server,该方法将不起作用

安全策略

socket.io提供了两种安全策略

allowRequest

函数allowRequest有两个参数,第一个参数为收到的握手包(http.request)对象,作为判断依据, success), err是错误对象,success为boolean, false表示阻止建立连接

前端请求带上token

let socket = io('http://localhost:3000?token=abc') 
socket.on('connect', () => { 
 console.log('connect')
})
socket.on('connect_error', err => { 
 socket.disconnect()
 console.log('connect_error', err)
})

后端allowRequest根据token判断是否继续

let app = require('http').createServer() 
let io = require('socket.io')(app, { 
 allowRequest: (req, cb) => {
 if (req._query && req._query.token === 'abc') return cb(null, true)
 cb(null, false)
 }
});

origins

可以对源进行限制

1、实例化时限制源

let app = require('http').createServer() 
let io = require('socket.io')(app, { 
 origins: 'http://localhost:3000'
})

2、origins函数设置源

origins函数有两种形式

origins(string) : 设置运行的源

origins(string, fn(err, success)) : 通过函数判断源是否允许

io.origins('http://localhost:*')

io.origins((origin, cb) => { 
 if (origin === 'http://localhost:3000/') return cb(null, true)
 cb(null, false)
})

名称空间

名称空间用来对服务端/客户端的连接隔离,有些地方,也称呼名称空间(namespace)为通道(channel)。下面举例对其意义进行说明

我们需要实现一个协同应用,这个应用有两个功能:

  • 协同编辑: 多个用户可以同时编辑一个文档
  • 消息: 用户间可以发送消息

用socket.io实现这个应用,有如下几种形式

1、完全独立: 协同编辑有一个独立服务edit.socket.test ,消息系统一个独立服务message.socket.test

let editSocket = io('edit.socket.test') 
let messageSocket = io('message.socket.test')

2、名称空间: 只运行一个独立服务,通过名称空间进行隔离

let app = require('http').createServer() 
let io = require('socket.io')(app) 
let editServer = io.of('/edit') 
let messsageServer = io.of('/message') 
editServer.on('connection', socket => { 
 //编辑相关
})
messsageServer.on('connection', socket => { 
 /消息相关
})
let editSocket = io('socket.test/edit') 
let messageSocket = io('socket.test/message')

3、事件名约定: 通过为事件名添加进行隔离

let app = require('http').createServer() 
let io = require('socket.io')(app)

io.on('connection', socket => { 
 //编辑相关
 io.emit('edit:test')
 io.on('edit:test', data => {

 })
 //消息相关
 io.emit('message:test')
 io.on('message:test', data => {

 })
}

通过事件名约定程序的侵入性太大,不利于拆分和重组,不推荐。 而完全独立的模式需要使用两个socket连接,即浪费浏览器允许的并发连接数,又更多消耗服务器资源。使用名称空间即能实现很好的隔离,又不会对资源造成浪费。

默认名称空间

socket.io实例化时自动绑定路径为/的名称空间

let app = require('http').createServer() 
let io = require('socket.io')(app)

io.sockets // io.of('/').sockets 
io.emit // 代理io.of('/').emit, 类似函数有'to', 'in', 'use', 'send', 'write', 'clients', 'compress'

中间件

socket.io的名空间通过use注册中间件,中间件在客户端与服务端建立连接成功后,connet事件派发前调用一次。

利用中间件数据校验

io.use((socket, next) => { 
 if (socket.request.headers.cookie) return next()
 next(new Error('Authentication error'))
})

利用中间件提取或转换数据 io.use((socket, next) => {
getInfo(socket.request.query.id, (err, data) => { if (err) return next(err) socket.custom = data next() }) })

与allowRequest对比

allowRequest可以进行一些校验,提取,为什么还要需要中间件?

  • allowRequest传入的http.request实例,而中间件出入数据socket实例,socket实例包含request实例,且有更多信息
  • 中间件直接支持多个异步流程嵌套,而allowRequest需要自己实现

与connection事件对比

connection事件也传入socket,也可以进行数验,提取,为什么还要需要中间件?

  • 中间件直接支持多个异步流程嵌套,而allowRequest需要自己实现
  • 中间件成功后到connection事件发送成功前,socket.io还做了一些工作,比如把socket实例添加到connected对象中,加入聊天室等。如果因为权限中断连接,在中间件中处理更省资源.

聊天室

聊天室是对当前连接的socket集合根据特定规则进行归组,方便群发消息。可以类比QQ群的概率.

socket.join('room name') //进入 
socket.leave('room name') //退出
io.to('some room').emit('some event') // io.to与io.in同义,向某个聊天室的所有成员发送消息

默认聊天室

每个socket在连接成功后会自动创建一个默认个聊天室,这个聊天室的名字是当前socket的id,可以通过默认聊天室实现向特定用户发送消息

socket.on('say to someone', (id, msg) => { 
 socket.broadcast.to(id).emit('my message', msg)
})

消息发送

应答消息

普通消息不需要回应,而应答消息提供了应答机制

io.on('connection', socket => { 
 socket.emit('an event', { some: 'data' }) //普通消息

 socket.emit('ferret', 'tobi', function (data) { //应答消息
 console.log(data); // data will be 'woot'
 })
})
socket.on('ferret', (name, fn) => { 
 fn('woot')
})

压缩

socket.compress(true)启用压缩,调用后当前连接的所有数据在传递给客户端前都会进行压缩

volatile标志

socket.io在正常情况下对发送的消息进行追踪,确保消息发送成功,而设置volatile后发送消息,socket.io不会对消息追踪,消息可能丢失

分类

// 客户端发送消息
socket.emit('hello', 'can you hear me?', 1, 2, 'abc');

// 向所有连接的客户端(除了自己)发送消息
socket.broadcast.emit('broadcast', 'hello friends!');

// 向game聊天室发送消息,自己不算
socket.to('game').emit('nice game', "let's play a game");

// 同时向game1和game2聊天室发送消息,自己不算
socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");

// 向game聊天室的所有人发送消息
io.in('game').emit('big-announcement', 'the game will start soon');

// 发送消息到<socketid>客户端
socket.to(<socketid>).emit('hey', 'I just met you');

// 发送应答消息
socket.emit('question', 'do you think so?', function (answer) {});

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript 流畅动画实现原理
Sep 08 Javascript
学习ExtJS Window常用方法
Oct 07 Javascript
js字符串的各种格式的转换 ToString,Format
Aug 08 Javascript
jQuery中json对象的复制方式介绍(数组及对象)
Jun 08 Javascript
提高jQuery性能的十个诀窍
Nov 14 Javascript
JS实现日期加减的方法
Nov 29 Javascript
JS短路原理的应用示例 精简代码的途径
Dec 13 Javascript
js实现简洁大方的二级下拉菜单效果代码
Sep 01 Javascript
js+html5实现canvas绘制椭圆形图案的方法
May 21 Javascript
Auto.js自动收取自己和好友蚂蚁森林能量脚本
Jun 28 Javascript
用element的upload组件实现多图片上传和压缩的示例代码
Feb 12 Javascript
详细分析JavaScript中的深浅拷贝
Sep 17 Javascript
socket.io学习教程之基本应用(二)
Apr 29 #Javascript
socket.io学习教程之基础介绍(一)
Apr 29 #Javascript
Vue.js实现一个SPA登录页面的过程【推荐】
Apr 29 #Javascript
Vue.js在使用中的一些注意知识点
Apr 29 #Javascript
jQuery实现按比例缩放图片的方法
Apr 29 #jQuery
Vue开发中整合axios的文件整理
Apr 29 #Javascript
Vue.js教程之axios与网络传输的学习实践
Apr 29 #Javascript
You might like
PHP图像处理之使用imagecolorallocate()函数设置颜色例子
2014/11/19 PHP
PHP CodeIgniter框架的工作原理研究
2015/03/30 PHP
php实现读取和写入tab分割的文件
2015/06/01 PHP
JavaScript入门教程(3) js面向对象
2009/01/31 Javascript
jQuery Validation实例代码 让验证变得如此容易
2010/10/18 Javascript
运行Node.js的IIS扩展iisnode安装配置笔记
2015/03/02 Javascript
基于JS实现textarea中获取动态剩余字数的方法
2016/05/25 Javascript
老生常谈JavaScript数组的用法
2016/06/10 Javascript
JavaScript中子对象访问父对象的方式详解
2016/09/01 Javascript
JavaScript中定时控制Throttle、Debounce和Immediate详解
2016/11/17 Javascript
D3.js中强制异步文件读取同步的几种方法
2017/02/06 Javascript
还不懂递归?读完这篇文章保证你会懂
2018/07/29 Javascript
electron制作仿制qq聊天界面的示例代码
2018/11/26 Javascript
详解从0开始搭建微信小程序(前后端)的全过程
2019/04/15 Javascript
详解vue 2.6 中 slot 的新用法
2019/07/09 Javascript
十分钟教你上手ES2020新特性
2020/02/12 Javascript
JS绘图Flot如何实现可选显示曲线图功能
2020/10/16 Javascript
vue-router定义元信息meta操作
2020/12/07 Vue.js
vue+elementUI动态增加表单项并添加验证的代码详解
2020/12/17 Vue.js
[43:53]OG vs EG 2019国际邀请赛淘汰赛 胜者组 BO3 第三场 8.22
2019/09/05 DOTA
Flask框架学习笔记(一)安装篇(windows安装与centos安装)
2014/06/25 Python
python 的列表遍历删除实现代码
2020/04/12 Python
Python中利用Scipy包的SIFT方法进行图片识别的实例教程
2016/06/03 Python
python正则-re的用法详解
2019/07/28 Python
对tensorflow中cifar-10文档的Read操作详解
2020/02/10 Python
python实现简单的tcp 文件下载
2020/09/16 Python
使用CSS3的appearance属性改变元素的外观的方法
2015/12/12 HTML / CSS
澳大利亚在线消费电子产品商店:TobyDeals
2020/01/05 全球购物
新加坡最早生产电动滑板车的制造商之一:FunsToTheFore
2020/09/08 全球购物
校友会欢迎辞
2014/01/13 职场文书
教师党员一句话承诺
2014/03/28 职场文书
党建工作先进材料
2014/05/02 职场文书
四风问题原因分析及整改措施
2014/10/24 职场文书
一篇文章学会Vue中间件管道
2021/06/20 Vue.js
PHP设计模式(观察者模式)
2021/07/07 PHP
Python初识逻辑与if语句及用法大全
2021/08/07 Python