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

发布时间 - 2026-01-11 00:54:19    点击率:

前言

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) {}); 

总结

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


# socket.io教程  # socket.io  # 教程  # node.js  # socket.io学习教程之基本应用(二)  # socket.io学习教程之基础介绍(一)  # node.js中的Socket.IO使用实例  # node.js中Socket.IO的进阶使用技巧  # 基于socket.io和node.js搭建即时通信系统  # node.js中的socket.io入门实例  # 使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室  # node中socket.io的事件使用详解  # node.js中使用socket.io的方法  # node.js中的socket.io的广播消息  # 聊天室  # 发送消息  # 客户端  # 多个  # 绑定  # 将不  # 服务端  # 安全策略  # 情况下  # 加载  # 有两个  # 很好  # 第一个  # 不需要  # 两种  # 我们可以  # 可以通过  # 对其  # 太大  # 会对 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】  使用Dockerfile构建java web环境  悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音  iOS发送验证码倒计时应用  JS去除重复并统计数量的实现方法  Linux网络带宽限制_tc配置实践解析【教程】  Laravel怎么实现一对多关联查询_Laravel Eloquent模型关系定义与预加载【实战】  个人摄影网站制作流程,摄影爱好者都去什么网站?  手机网站制作与建设方案,手机网站如何建设?  微博html5版本怎么弄发超话_超话进入入口及发帖格式要求【教程】  Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制  Linux后台任务运行方法_nohup与&使用技巧【技巧】  邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?  如何正确选择百度移动适配建站域名?  家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?  车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?  Laravel如何使用Service Container和依赖注入?(代码示例)  动图在线制作网站有哪些,滑动动图图集怎么做?  浅谈redis在项目中的应用  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)  *服务器网站为何频现安全漏洞?  phpredis提高消息队列的实时性方法(推荐)  php485函数参数是什么意思_php485各参数详细说明【介绍】  如何在建站之星网店版论坛获取技术支持?  西安专业网站制作公司有哪些,陕西省建行官方网站?  Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】  Swift中循环语句中的转移语句 break 和 continue  Laravel如何为API编写文档_Laravel API文档生成与维护方法  Laravel如何实现用户注册和登录?(Auth脚手架指南)  如何在Windows 2008云服务器安全搭建网站?  Laravel如何配置和使用缓存?(Redis代码示例)  Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程  Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  移动端手机网站制作软件,掌上时代,移动端网站的谷歌SEO该如何做?  Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  香港服务器部署网站为何提示未备案?  Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】  怎么用AI帮你设计一套个性化的手机App图标?  Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】  JavaScript数据类型有哪些_如何准确判断一个变量的类型  如何在搬瓦工VPS快速搭建网站?  nodejs redis 发布订阅机制封装实现方法及实例代码  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优  如何撰写建站申请书?关键要点有哪些?  英语简历制作免费网站推荐,如何将简历翻译成英文?  Laravel如何创建和注册中间件_Laravel中间件编写与应用流程  如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】