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的prototype属性
Feb 11 Javascript
jquery parent和parents的区别分析
Oct 02 Javascript
Node.js中使用计时器定时执行函数详解
Aug 15 Javascript
推荐6款基于jQuery实现图片效果插件
Dec 07 Javascript
jQuery validate验证插件使用详解
May 11 Javascript
JS中如何比较两个Json对象是否相等实例代码
Jul 13 Javascript
JavaScript实现的搜索及高亮显示功能示例
Aug 14 Javascript
Vue中如何实现proxy代理
Apr 20 Javascript
angularjs手动识别字符串中的换行符方法
Oct 02 Javascript
JS 实现获取验证码 倒计时功能
Oct 29 Javascript
js实现双人五子棋小游戏
May 28 Javascript
vue接通后端api以及部署到服务器操作
Aug 13 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
《星际争霸重制版》兵种对比图鉴
2020/03/02 星际争霸
磨咖啡豆的密诀
2021/03/03 冲泡冲煮
网友原创的PHP模板类代码
2008/09/07 PHP
那些年我们错过的魔术方法(Magic Methods)
2014/01/14 PHP
PHP冒泡算法详解(递归实现)
2014/11/10 PHP
JavaScript 异步调用框架 (Part 1 - 问题 &amp; 场景)
2009/08/03 Javascript
Jquery UI震动效果实现原理及步骤
2013/02/04 Javascript
基于javascript 闭包基础分享
2013/07/10 Javascript
js sort 二维数组排序的用法小结
2014/01/24 Javascript
js获取 type=radio 值的方法
2014/05/09 Javascript
JSON+HTML实现国家省市联动选择效果
2014/05/18 Javascript
一个JavaScript防止表单重复提交的实例
2014/10/21 Javascript
jQuery实现Twitter的自动文字补齐特效
2014/11/28 Javascript
JavaScript设计模式初探
2016/01/07 Javascript
js实现各种复制到剪贴板的方法(分享)
2016/10/27 Javascript
AngularJS中isolate scope的用法分析
2016/11/22 Javascript
jstree的简单实例
2016/12/01 Javascript
Node.js 8 中的 util.promisify的详解
2017/06/12 Javascript
js单页hash路由原理与应用实战详解
2017/08/14 Javascript
angular 表单验证器验证的同时限制输入的实现
2019/04/11 Javascript
详解jquery和vue对比
2019/04/16 jQuery
详解vue-cli3多页应用改造
2019/06/04 Javascript
JavaScript面向对象核心知识与概念归纳整理
2020/05/09 Javascript
node.js爬虫框架node-crawler初体验
2020/10/29 Javascript
Python如何判断数独是否合法
2016/09/08 Python
python pip安装包出现:Failed building wheel for xxx错误的解决
2019/12/25 Python
python3.7 openpyxl 在excel单元格中写入数据实例
2020/09/01 Python
python爬虫中的url下载器用法详解
2020/11/30 Python
上班上网检讨书
2014/01/29 职场文书
2014两会学习心得:榜样精神伴我行
2014/03/17 职场文书
元宵节主持词
2014/03/25 职场文书
房地产广告策划方案
2014/05/15 职场文书
2015年采购员工作总结
2015/04/27 职场文书
CSS filter 有什么神奇用途
2021/05/25 HTML / CSS
JVM上高性能数据格式库包Apache Arrow入门和架构详解(Gkatziouras)
2021/05/26 Servers
三种方式清除vue路由跳转router-link的历史记录
2022/04/10 Vue.js