socket.io__中文版文档

798 阅读22分钟

一、概述

1、如何使用

安装

$ npm install socket.io

使用Node http服务器搭建

服务器端(app.js)

var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');

app.listen(80);

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

客户端(index.html)

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

使用Express3/4

服务器端(app.js)

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

server.listen(80);

app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

客户端(index.html)

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

使用Express 2.X

服务器端(app.js)

var app = require('express').createServer();
var io = require('socket.io')(app);

app.listen(80);

app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

客户端(index.html)

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

发送和接收事件推送

Socket.io允许你触发响应自定义的事件,除了connectmessagedisconnect这些事件的名字不能使用之外,你可以触发任何自定义的事件名称。

服务器端

// 注意,io(<端口号>) 将为你创建一个http服务。
var io = require('socket.io')(80);

io.on('connection', function (socket) {
  io.emit('this', { will: 'be received by everyone'});

  socket.on('private message', function (from, msg) {
    console.log('I received a private message by ', from, ' saying ', msg);
  });

  socket.on('disconnect', function () {
    io.emit('user disconnected');
  });
});

创建你自己的路由

如果你只需要掌控一个应用的全部的消息和触发的事件,那么使用默认的**/**命名空间即可。如果你想要利用第三方代码,或者分享你的代码给别人,socket.io提供了一种命名一个socket的途径。

使用多路由控制一条单一的连接是有好处的。比如下方的示例代码,客户端发起两个WebSocket连接,而服务器端使用多路由技术仅仅只需要建立一个连接。

服务器端(app.js)

var io = require('socket.io')(80);
var chat = io
  .of('/chat')
  .on('connection', function (socket) {
    socket.emit('a message', {
        that: 'only'
      , '/chat': 'will get'
    });
    chat.emit('a message', {
        everyone: 'in'
      , '/chat': 'will get'
    });
  });

var news = io
  .of('/news')
  .on('connection', function (socket) {
    socket.emit('item', { news: 'item' });
  });

客户端(index.html)

<script>
  var chat = io.connect('http://localhost/chat')
    , news = io.connect('http://localhost/news');
  
  chat.on('connect', function () {
    chat.emit('hi!');
  });
  
  news.on('news', function () {
    news.emit('woot');
  });
</script>

发送不确定能否准确送达到客户端的消息

有些时候,一些发送的消息会在传输过程中不慎丢失(由于网络故障或者其他问题导致,或者由于他们是通过长连接轮询的方式存在与请求-响应循环列表中)。

这种情况下,你可能想要发送这样的一类消息,叫做易挥发消息(volatile message)。

服务器端

var io = require('socket.io')(80);

io.on('connection', function (socket) {
  var tweets = setInterval(function () {
    getBieberTweet(function (tweet) {
      socket.volatile.emit('bieber tweet', tweet);
    });
  }, 100);

  socket.on('disconnect', function () {
    clearInterval(tweets);
  });
});

正在发送和正在接受的数据(消息确认机制 acknowledgements)

有些时候,客户端会需要确认向服务器端发送的事件是否在服务器端正确执行了。

为了能够实现这一功能,只需简单的通过一个回调函数,放置与**.send()或者.emit()方法的最后一个参数即可,值得一提的是,当你使用.emit()**,这个确认是有你来完成的,也就是说你可以在这里一直的发送数据。

服务器端(app.js)

var io = require('socket.io')(80);

io.on('connection', function (socket) {
  socket.on('ferret', function (name, fn) {
    fn('woot');
  });
});

客户端(index.html)

<script>
  var socket = io(); // TIP: io() with no args does auto-discovery
  socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
    socket.emit('ferret', 'tobi', function (data) {
      console.log(data); // data will be 'woot'
    });
  });
</script>

广播消息给除当前客户端之外的所有在线客户端

为了实现广播消息这一功能,简单的添加一个broadcast标志给emitsend方法的calls,广播意味着发送一个给所有socket(客户端)的消息,不过除了触发这一广播消息的socket(客户端)之外。

服务器端(app.js)

var io = require('socket.io')(80);

io.on('connection', function (socket) {
  socket.broadcast.emit('user connected');
});

客户端(index.html)

<script>
  var socket = io('http://localhost/');
  socket.on('connect', function () {
    socket.send('hi');

    socket.on('message', function (msg) {
      // my msg
    });
  });
</script>

二、服务器端API

Server

通过 require('socket.io') 暴露。


new Server(httpServer[, options]);

const Server = require('socket.io'); new Server(httpServer[, options])

参数说明:

  • httpServer (http.Server) 需要绑定的服务。
  • options (对象)
    • path (字符串):捕获webSocket连接的路径名,默认为( / socket.io)。
    • serverClient (布尔型):是否为本地文件提供服务,默认为(true)。
    • adapter (Adapter对象):使用哪一个适配器对象,默认的指向Adapter类的一个实例,详情跳转至socket.io-adapter
    • origins (字符串):规定被允许的域,默认为(*) 。
    • parser (Parser对象):指向一个parser对象,默认使用与socket.io相关联的socket.io-parser

使用new关键字和不使用new关键字均可实例化一个socket连接。

const io = require('socket.io')();
// or
const Server = require('socket.io');
const io = new Server();

对于engine.io,使用相同的配置项即可,详情查看engine.io的配置项参考

其他的配置项:

  • pingTimeout (数值型):客户端在没有收到服务器端的响应时,等待多少毫秒数,,默认是60000毫秒(即1分钟)。
  • pingInterval (数值型):服务器端在发送响应包前延迟多少毫秒,默认为25000毫秒(即25秒)。

这两个参数将会影响的是响应延迟,客户端在知道服务不可用之前仍然需要等待一段时间。举个例子,如果下行TCP连接没有关闭,大概是由于网络故障,但是客户端不得不等待pingTimeout+pingInterval这个长的毫秒数才能得知disconnect(未连接成功)这一事件。

  • transports (Array包含一系列字符串元素的数组):这一选项规定了允许哪些连接方式,默认的( ['polling','websocket'] )。

注意:这一点很重要,默认的,会使用长轮询的连接方式作为第一手方案,随后如果设备支持的话会升级到使用WebSocket,如果transports选项的值设置为**['websocket']**,则意味着直接使用WebSocket方式建立连接,并且如果这一连接方式不能使用,也不会自动切换到备用的链接方案(polling),因此目前建议使用默认的设置即可,除非你明白确信使用场景和你想要做什么。

const server = require('http').createServer();

const io = require('socket.io')(server, {
  path: '/test',
  serveClient: false,
  // below are engine.IO options
  pingInterval: 10000,
  pingTimeout: 5000,
  cookie: false
});

server.listen(3000);

new Server(port[, options]);

  • port (数值型):要监听的端口号(如此一来 httpServer将会被自动创建)。
  • options (对象):同上方的配置
const server = require('http').createServer();

const io = require('socket.io')(3000, {
  path: '/test',
  serveClient: false,
  // below are engine.IO options
  pingInterval: 10000,
  pingTimeout: 5000,
  cookie: false
});

new Server(options);

  • options (对象):同上方配置项
const io = require('socket.io')({
  path: '/test',
  serveClient: false,
});

// either
const server = require('http').createServer();

io.attach(server, {
  pingInterval: 10000,
  pingTimeout: 5000,
  cookie: false
});

server.listen(3000);

// or
io.attach(3000, {
  pingInterval: 10000,
  pingTimeout: 5000,
  cookie: false
});

server.sockets

  • (命名空间) 默认的命名空间为**/**。

server.serverClient([value]);

  • value (布尔型)
  • Return Server | Boolean

如果value的值为true,则绑定的服务将会对本地文件提供服务。默认的为true。这个方法在attach函数被调用之后再调用它不会产生任何效果。如果没有提供任何参数,这个方法将会返回当前的值。

// 通过http server的情况可以直接这样写。
const io = require('socket.io')(http, { serveClient: false });

// 或者不通过server的情况,你可以这样调用serverClient方法,记得需要在attach之前调用。
const io = require('socket.io')();
io.serveClient(false);
io.attach(http);

server.path([value]);

  • value (字符串)
  • Return Server | String

设置路径值,指定哪一个engine.io和静态文件将被提供服务。默认的路径为**/socket.io**。如果没有提供参数这个方法将会返回当前的路径值。

const io = require('socket.io')();
io.path('/myownpath');

// 客户端
const socket = io({
  path: '/myownpath'
});

server.adapter([value]);

  • value (Adapter)
  • Return Server | Adapter

设置适配器的值,默认的指向一个Adapter的实例。如果没有提供参数则返回当前的值。

const io = require('socket.io')(3000);
const redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

server.origins([value]);

  • value (字符串)
  • Return Server | String

设置被允许的域的值,默认的任何域都被允许。如果没有提供参数这个方法将会返回当前的值。

io.origins(['foo.example.com:443']);

server.origins(fn);

  • fn (Function)
  • Return Server

提供一个函数,这个函数将携带两个参数,分别是客户端请求的origin:String和一个回调函数callback(err, succcess) ,开发者可以判断origin这个参数是否为希望接收请求的域,success是一个布尔值,代表着提供的这个域是否被允许,如果允许该域则填入true,如果不允许则填入false即可,err参数如无可填入null

潜在的缺点

  • 在一些情况下,当无法准确的判断域时,将会自动的使用(*),即全部允许。
  • 这个函数将会对所有的请求都执行一次,这个函数将尽可能快的执行完毕。
  • 如果socket.io和Express一起使用,CORS头信息将会仅对socket.io的请求有效,因此Express可以使用Cors
io.origins((origin, callback) => {
  if (origin !== 'https://foo.example.com') {
    return callback('origin not allowed', false);
  }
  callback(null, true);
});

server.attach(httpServer[, options]);

  • httpServer (httpServer)要依附的http服务
  • option (对象)

依附于这个Server到一个httpServer上的engine.io实例,并携带options配置项(可有可无)。


server.listen(httpServer[, options]);

和**server.attach(port[, options])**功能相同。

server.bind(engine);

  • engine (engine.Server)
  • Return Server

推荐这样使用,绑定这一服务到指定的Engine.io实例上。


server.onconnections(socket);

  • socket (engine.Socket)
  • Return Server

推荐使用,创建一个新的socket.io客户端。


server.of(nsp)

  • nsp (字符串)
  • Return 命名空间

通过路径名称来标志nsp(命名空间),初始化并且返回给定的命名空间。如果命名空间已经被初始化了,将立即返回。

const adminNamespace = io.of('/admin');

server.close([callback]);

  • callback (Function)

关闭这个socket.io服务。这个回调函数是可选的(可填可不填),这个回调函数将在所有连接被关闭后执行。

const Server = require('socket.io');
const PORT   = 3030;
const server = require('http').Server();

const io = Server(PORT);

io.close(); // Close current server

server.listen(PORT); // PORT is free to use

io = Server(server);

Namespace

代表一些列的sockets的链接所指向的作用域的标志,这些标志通过路径名来唯一确定(比如**/chat**这一路径名,或者叫做命名空间)。

一个客户端总是先尝试着连接到**/**(主要的命名空间),然后潜在的连接到其他可用的命名空间(当使用相同的下行连接和多路由机制时)。


namespace.name

  • (String)

命名空间标识符属性。


namespace.connected

  • (Object)

连接到这一命名空间的socket对象的哈希码们,索引为id


namespace.adapter

  • (Adapter)

Adapter被用于命名空间,当使用给予redis的Adapter时是很有用的。它将会通过你的集群暴露一些方法来管理sockets和房间。

注意:注命名空间的适配器可以这样使用,**io.of('/').adapter。


namespace.to(room);

  • room (String)
  • Return Namespace for chaining

设置修改器,用来将随后的事件发射到到指定的房间号,这样只有存在于指定房间的socket客户端才可接受到广播消息。

为了触发多个房间,你可以多次调用to方法。

const io = require('socket.io')();
const adminNamespace = io.of('/admin');

adminNamespace.to('level1').emit('an event', { some: 'data' });

namespace.in(room)

用法同namespace.to(room)


namespace.emit(eventName[, ...args])

  • eventName (String)
  • args

触发一个事件给所有的连接中的客户端。下方代码的两种示例作用是等价的。

const io = require('socket.io')();
io.emit('an event sent to all connected clients'); // main namespace

const chat = io.of('/chat');
chat.emit('an event sent to all connected clients in chat namespace');

注意:从命名空间触发的事件不支持消息送达确认。

namespace.client(callback)

  • callback (Function)

获取一系列连接到当前命名空间(路由)的客户端ID(会穿越所有节点)。

const io = require('socket.io')();
io.of('/chat').clients((error, clients) => {
  if (error) throw error;
  console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]
});

示例,获得所有在指定命名空间的房间里的客户端们。

io.of('/chat').in('general').clients((error, clients) => {
  if (error) throw error;
  console.log(clients); // => [Anw2LatarvGVVXEIAAAD]
});

和广播一样,默认的,将获取所有从默认的**/**命名空间过来的客户端们。

io.clients((error, clients) => {
  if (error) throw error;
  console.log(clients); // => [6em3d4TJP8Et9EMNAAAA, G5p55dHhGgUnLUctAAAB]
});

namespace.use(fn)

  • fn (Function)

注册一个中间件,这个函数将对所有流经的socket执行操作,并且还会已接受传参的方式获得流经的socket,并且流经到当前中间件之后,可以可选择的确定是否流经到下一个中间件。

当错误信息经过中间件,回调函数将会发送一个特殊的错误信息给客户端。

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

Event: 'connect'

  • socket (socket) 客户端的socket连接实例

当有一个来自客户端的连接时触发该事件。

io.on('connect', (socket) => {
  // ...
});

io.of('/admin').on('connect', (socket) => {
  // ...
});

Event: 'connection'

用法同Event: 'connect'


Flag: 'Volatile'

设置修改器,将随后的事件触发导向这样一种情况:即如果当客户端没有做好接受信息的准备时(可能由于网络故障或者其他问题导致的,或者连接方式采用长轮询的方式,而恰好响应接受消息的事件此时还在请求-响应循环列表中未被触发),那么允许服务器发送的数据丢失。

io.volatile.emit('an event', { some: 'data' }); // the clients may or may not receive it

Flag: 'local'

设置修改器,将随后的事件导向这样一种情况:即事件数据仅广播给当前节点(当使用了Redis adapter)。

io.local.emit('an event', { some: 'data' });

Socket

socket是与客户端浏览器交互的基石。socket属于一个确定的命名空间(默认为**/**),并且使用下行客户端沟通讯息。

值得注意的是,这里所指的socket和下行TCP/IP的socket不是一回事儿,这里所指的socket只是一个类名而已。

在每一个命名空间内,你可以定义任意的频道(被叫做房间room的东西),如此socket就可以加入房间或者离开房间。房间的机制使得服务器端可以同时给一组socket广播消息。

socket类集成了EventEmitter,socket类重写了emit方法,并且不会修改其他的EventEmitter方法、这里所有的以EventMitter的形式出现的方法们均是通过EventEmitter实现。

socket.id

  • (返回字符串)

一个独一无二的针对当前会话socket的标志,来自下行客户端。


socket.rooms

  • (返回对象)

遗传哈希字符串,用来标志当前客户端所在的房间号,通过房间名称建立索引。

io.on('connection', (socket) => {
  socket.join('room 237', () => {
    let rooms = Objects.keys(socket.rooms);
    console.log(rooms); // [ <socket.id>, 'room 237' ]
  });
});

socket.client

  • (Client)

下行客户端对象的引用。


socket.conn

  • (engine.Socket)

下行客户端传输连接的引用(engine.io socket 对象),这个允许进入到IO的传输层,不过仍然是实际的TCP/IP socket的一种抽象表示。


socket.handshake

  • (对象)

握手(handshake)细节:

{
  headers: /* the headers sent as part of the handshake */,
  time: /* the date of creation (as string) */,
  address: /* the ip of the client */,
  xdomain: /* whether the connection is cross-domain */,
  secure: /* whether the connection is secure */,
  issued: /* the date of creation (as unix timestamp) */,
  url: /* the request URL string */,
  query: /* the query object */
}

用例

io.use((socket, next) => {
  let handshake = socket.handshake;
  // ...
});

io.on('connection', (socket) => {
  let handshake = socket.handshake;
  // ...
});

socket.use(fn)

  • fn (Function)

注册中间件,当任何讯息流经该中间件时执行中间件中的内容,该中间件会接受参数,也可以判断是否阻断后续中间件的执行。

当发生错误,错误将会通过中间件的回调函数,直接发送一个特殊的错误数据包到客户端。

io.on('connection', (socket) => {
  socket.use((packet, next) => {
    if (packet.doge === true) return next();
    next(new Error('Not a doge error'));
  });
});

socket.send([...args][, ack])

  • args
  • ack (Function)
  • Return Socket

发送一个message事件,


socket.emit(eventName[, ...args][, ack])

(重写 EventEmitter.emit方法)

  • eventName (字符串)
  • args
  • ack (Function)
  • Return Socket

通过事件名来触发事件给指定的socket,任意多的参数都可被传入,支持所有可序列化的数据结构。包括Buffer

socket.emit('hello', 'world');
socket.emit('with-binary', 1, '2', { 3: '4', 5: new Buffer(6) });

其中ack参数是可选的(用意确认客户端是否接受到讯息,或者对信息做处理并返回给服务器端),并且将被客户应答。

io.on('connection', (socket) => {
  socket.emit('an event', { some: 'data' });

  socket.emit('ferret', 'tobi', (data) => {
    console.log(data); // data will be 'woot'
  });

  // the client code
  // client.on('ferret', (name, fn) => {
  //   fn('woot');
  // });

});

socket.on(eventName, callback)

(继承子EventEmitter

  • eventName (字符串)
  • callback (Function)
  • Return Socket\

为给定的事件注册一个新的事件处理器。

socket.on('news', (data) => {
  console.log(data);
});
// with several arguments
socket.on('news', (arg1, arg2, arg3) => {
  // ...
});
// or with acknowledgement
socket.on('news', (data, callback) => {
  callback(0);
});

socket.once(eventName, listener)


socket.removeListener(eventName, listener)


socket.removeAllListener([eventName])


socket.eventNames()

继承自EventEmitter(还有其他在这里未提及的方法),查看Node.js的官方文档对events模块的说明。


socket.join(room[, callback])

  • room (字符串)
  • callback (Function)
  • Return Socket for chaining

添加客户端到room房间内,并且执行可选择的回调函数。

io.on('connection', (socket) => {
  socket.join('room 237', () => {
    let rooms = Objects.keys(socket.rooms);
    console.log(rooms); // [ <socket.id>, 'room 237' ]
    io.to('room 237', 'a new user has joined the room'); // broadcast to everyone in the room
  });
});

加入房间的过程被Adapter适配器处理。

为了更方便开发者,每一个socket自动的通过他自己的id标志创建了一个只属于他自己的房间,这样做,使得当前socket和其他socket之间的广播变得更容易。

io.on('connection', (socket) => {
  socket.on('say to someone', (id, msg) => {
    // send a private message to the socket with the given id
    socket.to(id).emit('my message', msg);
  });
});

socket.leave(room[, callback])

  • room (字符串)
  • callback (Function)
  • Return Socket for chaining

从指定的房间里移除客户端,并且可选择的执行一个异常回调函数。

与当客户端的连接丢失后,会自动的将其从房间移除


socket.to(room)

  • room (字符串)
  • Return Socket for chaining

设置修改器,使得随后的事件导向这样的一种情况:即仅向当前房间的客户端广播消息(主动广播消息的一方除外)。

为了能对多个房间触发同一个广播,你需要给多个房间链式的执行几次to方法。

io.on('connection', (socket) => {
  // to one room
  socket.to('others').emit('an event', { some: 'data' });
  // to multiple rooms
  socket.to('room1').to('room2').emit('hello');
  // a private message to another socket
  socket.to(/* another socket id */).emit('hey');
});

注意:广播消息下执行emit方法,是无法传入确认消息是否接收的回调函数的,原因你懂的。


socket.in(room)

用法同 socket.to(room)


socket.compress(value)

  • value (布尔型)是否对数据包进行压缩
  • Return Socket for chaining

设置修改器,将随后的事件导向这样一种情况,仅对设置为true的数据进行压缩,如果不调用该方法,默认为true

io.on('connection', (socket) => {
  socket.compress(false).emit('uncompressed', "that's rough");
});

socket.disconnect(close)

  • close (布尔型)是否关闭下行连接
  • Return Socket

关闭对客户端的链接,如果close的值为true,则关闭下行连接,否则,仅仅关闭命名空间。

io.on('connection', (socket) => {
  socket.compress(false).emit('uncompressed', "that's rough");
});

Flag: 'broadcast'

设置修改器,是的随后的事件导向这样一种情况,即除了主动广播消息的客户端以外,将随后的事件消息广播给所有的socket。

io.on('connection', (socket) => {
  socket.broadcast.emit('an event', { some: 'data' }); // everyone gets it but the sender
});

Flag: 'volatile'

设置修改器,使得随后的事件导向这样一种情况,允许客户端在没有做好接受数据的准备时丢失消息数据,详细翻译去上边找哈~。

io.on('connection', (socket) => {
  socket.volatile.emit('an event', { some: 'data' }); // the client may or may not receive it
});

Event: 'disconnect'

  • reason (字符串)丢失连接的原因(客户端与服务器端相同)

丢失连接时。

io.on('connection', (socket) => {
  socket.on('disconnect', (reason) => {
    // ...
  });
});

Event: 'disconnecting'

  • reason (字符串) 丢失连接的原因(客户端与服务器端相同)

当客户端丢失连接后执行(此时还为离开房间rooms)。

io.on('connection', (socket) => {
  socket.on('disconnecting', (reason) => {
    let rooms = Object.keys(socket.rooms);
    // ...
  });
});

这里有一些保留事件(connectnewListenerremoveListener),这些名臣不能用与事件自定义名称。


Client

client类代表从客户端过来的传输连接。一个客户端和属于不同命名空间的多路复用的socket有关联,即client客户端仅一个,而client可以通过访问不同的命名空间创建多个socket连接。


client.conn

  • (engine.Socket)

下行engine.io连接的引用。


client.request

  • (请求)

可以看作为一个代理,返回请求的引用。了解请求头信息,如Cookie或者User-Agent的知识是有帮助的。


三、客户端API

IO

如果你使用的是标准化的JS库,则暴露为io这一命名空间;如果你是用Node 编译,则使用require('socket.io-client')

<script src="/socket.io/socket.io.js"></script>
<script>
  const socket = io('http://localhost');
</script>
导入socket.io模块
const io = require('socket.io-client');
// or with import syntax
import io from 'socket.io-client';

io.protocol

  • (数值型)

表示协议版本号。


io(url[, options])

  • url (字符串)默认的指向widnow.location
  • option (Object)
    • forceNew (布尔型)是否重用当前已存在的链接。

\

  • Return Socket

使用给定的URL创建一个新的Manager,并且尝试重用一个已存在的Manager等待随后调用,除非multiplex的选项为false,作用同force new connection':true或者forceNew: true

一个新的socket实例将返回命名空间指定的socket,该命名空间由URL指定,默认的为**/ 。举个栗子,如果urlhttp://localhost/users**,则将会对**http://localhost**进行传输连接,并且[http://Socket.IO](link.zhihu.com/?target=htt…

你也也可以提供查询参数,直接将查询参数添加到url上即可:

http://localhost/users?token=abc

详情查看new Manager(url[, options])


初始化示例


使用多路复用

默认的,当连接道不同的命名空间后一个单一的链接将会被使用。

const socket = io();
const adminSocket = io('/admin');
// a single connection will be established

注意:重用相同的命名空间将会创建两个连接:

const socket = io();
const socket2 = io();
// will also create two distinct connections

自定义path

const socket = io('http://localhost', {
  path: '/myownpath'
});

// server-side
const io = require('socket.io')({
  path: '/myownpath'
});

这里,socket连接到admin命名空间,使用自定义的路径mypath

请求地址看起来像这样:

localhost/mypath/?EIO=3&transport=polling&sid=<id>

命名空间将会作为数据的一部分被发送。

携带查询参数

const socket = io('http://localhost?token=abc');

// server-side
const io = require('socket.io')();

// middleware
io.use((socket, next) => {
  let token = socket.handshake.query.token;
  if (isValid(token)) {
    return next();
  }
  return next(new Error('authentication error'));
});

// then
io.on('connection', (socket) => {
  let token = socket.handshake.query.token;
  // ...
});

携带查询选项

const socket = io({
  query: {
    token: 'cde'
  }
});

查询内容可以在重新连接时更新:

socket.on('reconnect_attempt', () => {
  socket.io.opts.query = {
    token: 'fgh'
  }
});

携带额外的请求头 extraHeaders

仅当建立的是轮询连接时才起作用(默认为polling轮询),当使用websocket建立传输时,自定义的请求头将不会被添加,因为WebSockets握手信号不信任自定义的请求头(详细信息可以阅读WebSocket protocol RFC

const socket = io({
  transportOptions: {
    polling: {
      extraHeaders: {
        'x-clientid': 'abc'
      }
    }
  }
});

// 服务器端
const io = require('socket.io')();

// 中间件
io.use((socket, next) => {
  let clientId = socket.handshake.headers['x-clientid'];
  if (isValid(clientId)) {
    return next();
  }
  return next(new Error('authentication error'));
});

仅当通过websocket传输时

默认的,长轮询连接会被首次创建,随后升级到更好的传输方式(比如WebSocket)。

如果你喜欢挑战性,这一部分可以被跳过。

const socket = io({
  transports: ['websocket']
});

// on reconnection, reset the transports option, as the Websocket
// connection may have failed (caused by proxy, firewall, browser, ...)
socket.on('reconnect_attempt', () => {
  socket.io.opts.transports = ['polling', 'websocket'];
});

携带自定义的解析器

默认的解析器(详细内容查阅这里Socket.IO custom parsers

const parser = require('socket.io-msgpack-parser'); // or require('socket.io-json-parser')
const socket = io({
  parser: parser
});

// the server-side must have the same parser, to be able to communicate
const io = require('socket.io')({
  parser: parser
});

Manager

new Manager(url[, options])

  • url (字符串)
  • options (对象)
    • path (字符串) 命名路径,用来捕获服务器端的服务,默认为socket.io
    • reconnection (布尔型)是否自动重新建立连接,默认为true
    • reconnectionAttempts (Number) 尝试重连的次数,默认为无限次
    • reconnectionDelay (数值型) 重寻创建连接的延迟时长,默认为1000毫秒,受randomizationFactor正负加减的影响。比如默认的初始化延迟将在500至1500毫秒之间。
    • reconnectionDelayMax (数值型)最大的重连等待时间,默认为5000毫秒。每一次尝试都会以两倍的增量增加重连的时间。
    • randomizationFactor (数值型)默认为0.5,最小为0,最大为1.
    • timeout (数值型) connect_errorconnect_timeout事件触发前的延迟时间,默认为20000毫秒。
    • autoConnect (布尔型) 如果设置为fasle,你不得不手动调用manage.open函数。
    • query (对象):当连接到一个命名空间,额外的查询参数将被发送(随后可以到服务器端查找socket.handshake.query对象)。
    • parser (解析器):默认的为一个Parser实例,详情查看socket.io-parser
    • Return Manager

\

这一选项同样可通过engine.io-client初始化,查看参数


manager.reconnection([value])

  • value (布尔型)
  • Return Manager | Boolean

设置reconnection选项,或者如果没有传递参数则直接返回它。


manager.reconnectionAttempts([value])

  • value (数值型)
  • Return Manager | Number

设置reconnectionAttempts选项,或者当没有参数时直接返回。


manager.reconnectionDelay([value])

  • value (数值型)
  • Return Manager | Number

设置reconnectionDelay选项,或者当没有参数时直接返回。

manager.reconnectionDelayMax([value])

  • value (数值型)
  • Return Manager | Number

设置reconnectionDelayMax选项,或者当没有参数时直接返回。


manager.timeout([value])

  • value (数值型)
  • Return Manager | Number

设置timeout选项,或者当没有参数时直接返回。


manager.open([callback])

  • callback (Function)
  • Return Manager

如果manager使用autoConnect初始化为false,尝试运行一个新的连接。

这个回调函数参数时可选择的,并且将会在错误或者成功后被执行一次。


manager.connect([callback])

同**manager.open([callbakc])


manager.socket(nsp, options)

  • nsp (字符串)
  • options (对象)
  • Return Socket

使用给定的命名空间创建一个新的Socket连接。


Event: 'connect_error'

  • error (对象) 错误对象

触发连接错误事件。


Event: 'connect_timeout'

触发连接超时事件


Event: 'reconnect'

  • attempt (数值型)尝试重新连接的次数

触发成功连接的事件


Event: 'reconnect_attempt'

触发尝试重新连接


Event: 'reconnecting'

  • attempt (数值型) 重新连接尝试次数

触发成功重新连接事件


Event: 'reconnect_error'

  • error (对象)错误对象

触发当在reconnectionAttempts次数内无法重新连接的错误事件


Event: 'ping'

当请求ping发送到服务器端时。


Event: 'pong'

  • ms (数值型) 自ping至pong经过的毫秒数

当接受到服务器端的pong时触发。


Sokect

socket.id

  • (字符串)

标志socket session一个独一无二的符号,在客户端连接到服务器后被设置。

const socket = io('http://localhost');

console.log(socket.id); // undefined

socket.on('connect', () => {
  console.log(socket.id); // 'G5p5...'
});

socket.open()

  • Returns Socket

手动打开socket

const socket = io({
  autoConnect: false
});

// ...
socket.open();

同样,也可以重新连接:

socket.on('disconnect', () => {
  socket.open();
});

socket.connect()

用法同socket.open()


socket.send([...args][, ack])

  • args
  • ack (Function)
  • Returns Socket

发送一个message事件,详细查看socket.emit(eventName[, ...args][, ack]).


socket.emit(eventName[, ...args][, ack])

  • eventName (字符串)
  • args
  • ack (Function)
  • Returns Socket

通过提供的name时间名称向socket标志发送事件,任何其他的参数都可被包含,所有可被序列化的数据结构都支持,包括Buffer。

socket.emit('hello', 'world');
socket.emit('with-binary', 1, '2', { 3: '4', 5: new Buffer(6) });

ack参数将用来响应服务器用来确认消息的应答。

socket.emit('ferret', 'tobi', (data) => {
  console.log(data); // data will be 'woot'
});

// server:
//  io.on('connection', (socket) => {
//    socket.on('ferret', (name, fn) => {
//      fn('woot');
//    });
//  });

socket.on(eventName, callback)

  • eventName (字符串)
  • callback (Function)
  • Returns Socket

注册一个新的响应服务器事件的事件处理器。

socket.on('news', (data) => {
  console.log(data);
});

// with multiple arguments
socket.on('news', (arg1, arg2, arg3, arg4) => {
  // ...
});
// with callback
socket.on('news', (cb) => {
  cb(0);
});

这个socket实际上继承Emitter类的所有方法,比如**hashListener"",onceoff(用来移除一个事件监听器)。


socket.commpress([value])

  • value (布尔型)
  • Returns Socket

设置修改器,是否对向服务器传输的数据进行压缩。默认为true,即压缩。

socket.compress(false).emit('an event', { some: 'data' });

socket.close()

  • Returns Socket

手动关闭客户端对服务器的链接


socket.disconnect()

用法同 socket.close()


Event: 'connect'

连接成功后执行该函数。

socket.on('connect', () => {
  // ...
});

// note: you should register event handlers outside of connect,
// so they are not registered again on reconnection
socket.on('myevent', () => {
  // ...
});

Event: 'connect_error'

  • error (对象) 错误对象

连接错误触发事件处理器。

socket.on('connect_error', (error) => {
  // ...
});

Event: 'disconnect'

  • reason (字符串) 服务器或客户端丢失连接的原因

丢失连接时触发时间处理器

socket.on('disconnect', (timeout) => {
  // ...
});

Event: 'reconnect'

  • attempt (字符串) 重连次数

成功的重连时触发时间处理器

socket.on('reconnect', (timeout) => {
  // ...
});

Event: 'reconnect_attempt'

  • attempt (字符串) 重连次数

成功的重连时触发时间处理器

socket.on('reconnect_attempt', (timeout) => {
  // ...
});

Event: 'reconnecting'

  • attempt (字符串) 尝试重连次数

尝试重连时触发时间处理器

socket.on('reconnecting', (timeout) => {
  // ...
});

Event: 'reconnect_error'

  • attempt (字符串) 错误对象

重连错误时触发时间处理器

socket.on('reconnect_error', (timeout) => {
  // ...
});

Event: 'reconnect_failed'

socket.on('reconnect_failed', (timeout) => {
  // ...
});

Event: 'ping'

socket.on('ping', (timeout) => {
  // ...
});

Event: 'pong'

  • ms (数值型) 自ping到pong经历的毫秒数
socket.on('pong', (timeout) => {
  // ...
});

若有收获,就点个赞吧