Socket.io是什么
Socket.io是一个用于在浏览器和服务器之间进行实时,双向和基于事件的通信库。
Socket.io包装了websocket,在浏览器支持的情况下优先使用websocket进行连接,否则回退到HTTP长轮询的方式,解决了部分浏览器暂不支持websocket的问题。
安装方法
在node.js服务端使用时,通过npm安装
复制代码- npm install socket.io
在浏览器中使用时可通过两种方式
第一种
复制代码- // socket.io在服务端的程序会自动将客户端的文件在该路径下暴露出来,
- // 因此直接通过src标签引入即可
- <script src="/socket.io/socket.io.js"></script>
第二种
复制代码- // 在服务端...
- // 如果不想通过服务端暴露的文件引用,则可以禁用该功能
- const io = require('socket.io')({
- // 不启用
- serveClient: false
- });
- // 在客户端
- // 然后通过cdn等方式引入(减轻自己服务器的压力)
- <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
与Express一起使用
复制代码- const app = require('express')();
- const server = require('http').createServer(app);
- const options = { /* ... */ };
- const io = require('socket.io')(server, options);
-
- io.on('connection', socket => { /* ... */ });
-
- server.listen(3000);
基本概念
命名空间(namespace)
命令空间是一个通信的通道,它便于分割应用的逻辑
例如:将所有客户端分割成两个空间,一个是Default空间,一个是Admin空间。对于Default空间我们可以做一些通用的操作,而对于Admin空间,我们用于做一些专用的操作。
复制代码- // 在服务端...
- // 建立admin命名空间
- const adminNamespace = io.of('/admin');
-
- // 对admin命名空间中的所有socket使用该中间件
- adminNamespace.use((socket, next) => {
- // 例如做一些验证操作
- // ensure the user has sufficient rights
- next();
- });
-
- // 监听admin命名空间中所有客户端的connection事件
- adminNamespace.on('connection', socket => {
- ...
- });
-
- // 在客户端...
- // 连接admin命名空间
- const socket = io('/admin');
- // 监听connection事件
- socket.on('connection', socket => {
- ...
- });
默认命名空间
默认命名空间
/
, 所有的客户端都会默认的连接到这个命名空间
房间(room)
在每个命名空间内,可以定义任意的通道,称为“ Room”,套接字可以加入和退出
复制代码- // 在服务端...
- io.on('connection', socket => {
- // 客户端建立连接之后,将其加入房间
- socket.join('some room');
- // 使用同样的方式离开一个房间
- socket.leave('some room')
- });
默认房间
每一个socket都有一个随机分配的标识符
Socket#id
,每一个socket都会自动加入以其ID标识的房间
使用中间件
复制代码- // 为默认命名空间注册一个中间件
- io.use((socket, next) => {
- if (isValid(socket.request)) {
- next();
- } else {
- next(new Error('invalid'));
- }
- });
-
- // 为自定义的命名空间制定一个中间件
- io.of('/admin').use(async (socket, next) => {
- const user = await fetchUser(socket.handshake.query);
- if (user.isAdmin) {
- socket.user = user;
- next();
- } else {
- next(new Error('forbidden'));
- }
- });
-
- ================================================
- // 可以为一个命名空间注册多个中间件,他们会依次执行
- io.use((socket, next) => {
- next();
- });
-
- io.use((socket, next) => {
- next(new Error('thou shall not pass'));
- });
-
- io.use((socket, next) => {
- // 不会执行,因为前一个中间件返回了错误
- next();
- });
-
- // 如果next方法返回了Error,那么客户端会收到error事件
-
- ====================================================
- // 使用express中间件
- // 大多数express的中间件都需要与socket.io兼容,你只需要将这些中间件,用一个函数包起来
- const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);
-
- const session = require('express-session');
-
- io.use(wrap(session({ secret: 'cats' })));
-
- io.on('connect', (socket) => {
- const session = socket.request.session;
- });
发送消息
现在已经知道了命名空间和房间,然后就该往这些命名空间和房间发送消息了
复制代码- // 在服务端...
- // 监听连接事件
- io.on('connect', onConnect);
-
- // 连接成功后使用的函数
- function onConnect(socket){
-
- // 发送到这个刚刚连接成功的客户端
- 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)");
- // 发送到所有的客户端,包括发送者
- io.in('game').emit('big-announcement', 'the game will start soon');
- // 发送到所有在myNamespace命名空间的客户端,包括发送者
- io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');
-
- // 发送到在myNamespace命名空间中的room房间中的客户端,包括发送者
- io.of('myNamespace').to('room').emit('event', 'message');
- // 发送到指定socketId的客户端(私聊)
- io.to(socketId).emit('hey', 'I just met you');
-
- // 注意: `socket.to(socket.id).emit()` 不会如预期那样,
- // 发送消息给指定socket, 而是会把socket.id当作room的名称
- // 将消息发给名为socket.id的room的房间中的每个socket(除开发送者)
-
- // 发送消息,并且带有确认回调
- socket.emit('question', 'do you think so?', function (answer) {});
-
- // 发送消息,但不使用压缩
- socket.compress(false).emit('uncompressed', "that's rough");
-
- // sending a message that might be dropped if the client is not ready to receive messages
- // 发送一条不可靠消息,该消息可能由于客户端未准备好而丢失
- socket.volatile.emit('maybe', 'do you really need it?');
-
- // 指定要发送的数据是否具有二进制数据
- socket.binary(false).emit('what', 'I have no binaries!');
-
- // 发送到node上的所有客户端(使用多个node时)
- io.local.emit('hi', 'my lovely babies');
-
- // 发送到所有客户端
-
- io.emit('an event sent to all connected clients');
-
- };
评论已关闭