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 相关文章推荐
jquery mobile的触控点击事件会多次触发问题的解决方法
May 08 Javascript
js图片滚动效果时间可随意设定当鼠标移上去时停止
Jun 26 Javascript
javascript基于DOM实现权限选择实例分析
May 14 Javascript
如何实现移动端浏览器不显示 pc 端的广告
Oct 15 Javascript
jquery过滤特殊字符',防sql注入的实现方法
Aug 17 Javascript
Jquery遍历select option和添加移除option的实现方法
Aug 26 Javascript
Vuejs第七篇之Vuejs过渡动画案例全面解析
Sep 05 Javascript
[原创]javascript typeof id==='string'?document.getElementById(id):id解释
Nov 02 Javascript
Bootstrap基本组件学习笔记之分页(12)
Dec 08 Javascript
JavaScript使用正则表达式获取全部分组内容的方法示例
Jan 17 Javascript
vue中用动态组件实现选项卡切换效果
Mar 25 Javascript
JavaScript 日期时间选择器一些小结
Apr 02 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
支持oicq头像的留言簿(一)
2006/10/09 PHP
php dirname(__FILE__) 获取当前文件的绝对路径
2011/06/28 PHP
PHP与服务器文件系统的简单交互
2016/10/21 PHP
javascritp实现input输入框相关限制用法
2007/06/29 Javascript
Javascript中的var_dump函数实现代码
2009/09/07 Javascript
JQuery与Ajax常用代码实现对比
2009/10/03 Javascript
javascript与webservice的通信实现代码
2010/12/25 Javascript
javascript的内存管理详解
2013/08/07 Javascript
使用CamanJS在Web页面上处理图像的技巧
2015/08/18 Javascript
Node.js中Request模块处理HTTP协议请求的基本使用教程
2016/03/31 Javascript
js模态对话框使用方法详解
2017/02/16 Javascript
ajax+node+request爬取网络图片的实例(宅男福利)
2017/08/28 Javascript
vue实现在线学生录入系统
2020/05/30 Javascript
VSCode Vue开发推荐插件和VSCode快捷键(小结)
2020/08/08 Javascript
JavaScript中CreateTextFile函数
2020/08/30 Javascript
通过Python模块filecmp 对文件比较的实现方法
2018/06/29 Python
Pycharm+Python+PyQt5使用详解
2019/09/25 Python
python tkinter 设置窗口大小不可缩放实例
2020/03/04 Python
IE8下CSS3选择器nth-child() 不兼容问题的解决方法
2016/11/16 HTML / CSS
使用css3做0.5px的细线的示例代码
2018/01/18 HTML / CSS
HearthSong官网:儿童户外玩具、儿童益智玩具
2017/10/16 全球购物
Merrell美国官网:美国登山运动鞋品牌
2018/02/07 全球购物
css animation配合SVG制作能量流动效果
2021/03/24 HTML / CSS
创先争优活动心得体会
2014/09/04 职场文书
试用期转正后的自我评价
2014/09/21 职场文书
四风问题原因分析及整改措施
2014/10/24 职场文书
作风建设年度心得体会
2014/10/29 职场文书
2015年12.4全国法制宣传日活动总结
2015/03/24 职场文书
2015年文员个人工作总结
2015/04/09 职场文书
校园安全学习心得体会
2016/01/18 职场文书
2016基督教会圣诞节开幕词
2016/03/04 职场文书
导游词之南京夫子庙
2019/12/09 职场文书
vue中data改变后让视图同步更新的方法
2021/03/29 Vue.js
用python删除文件夹中的重复图片(图片去重)
2021/05/12 Python
一篇文章带你学习Mybatis-Plus(新手入门)
2021/08/02 Java/Android
PostgreSQL数据库创建并使用视图以及子查询
2022/04/11 PostgreSQL