服務端API
服務端
通過require('socket.io')引入
new Server(httpServer[, options])
- httpServer (http.Server) socket.io所綁定的服務器.
options (Object) - path (String): 捕獲路徑,默認為(/socket.io)
- serveClient (Boolean): 是否充當客戶端,默認為 (true)
- adapter (Adapter):要使用的適配器。默認為基于內存的socket.io附帶的Adapter實例。請參閱socket.io-adapte
- origins (String): 允許的源(*)
- parser (Parser): 所使用解析器。默認為socket.io附帶Parser的一個實例。
直接引用,或者通過new創建一個實例:
const io = require('socket.io')();
// or
const Server = require('socket.io');
const io = new Server();
傳遞給socket.io的選項總是相同地傳遞給創建的engine.io服務器。 你可以參考engin.io的相關設置選項
在這些選項中:
.pingTimeout(Number):沒有pong數據包考慮多少毫秒后關閉連接(60000)
.pingInterval(Number):在發送新的ping數據包之前多少ms(25000)
在客戶端知道服務器不再可用之前,這兩個參數將影響延遲。例如,如果由于網絡問題導致基礎TCP連接未正確關閉,則客戶端可能必須在獲取斷開連接事件之前等待pingTimeout + pingInterval毫秒。
注意:順序很重要。默認情況下,首先建立一個長輪詢連接,如果可能的話,然后升級到WebSocket。使用['websocket']意味著如果無法打開WebSocket連接,則不會有后備。
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(數字)要監聽的端口(將創建一個新的http.Server)
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.serveClient([value])
- value (Boolean)
- Returns Server|Boolean
如果value為true,則連接的服務器(請參閱Server#attach)將為客戶端文件提供服務。默認為true。調用attach后,此方法無效。如果未提供參數,則此方法返回當前值。
// pass a server and the `serveClient` option
const io = require('socket.io')(http, { serveClient: false });
// or pass no server and then you can call the method
const io = require('socket.io')();
io.serveClient(false);
io.attach(http);
server.path([value])
- value (String)
- Returns Server|String
設置engine.io和靜態文件將被提供的路徑值。默認為/socket.io。如果未提供參數,則此方法返回當前值。
const io = require('socket.io')();
io.path('/myownpath');
// client-side
const socket = io({
path: '/myownpath'
});
server.adapter([value])
- value (Adapter)
- Returns Server|Adapter
設置適配器值。默認為基于內存的socket.io附帶的Adapter實例。請參閱socket.io-adapter。如果未提供參數,則此方法返回當前值。
const io = require('socket.io')(3000);
const redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
server.origins([value])
- value (String)
- Returns Server|String
設置允許的來源值。默認允許任何來源。如果未提供參數,則此方法返回當前值。
io.origins(['foo.example.com:443']);
server.origins(fn)
- fn (Function)
- Returns Server
提供一個帶有兩個參數的函數origin:String和callback(error,success),其中success是一個布爾值,指示源是否被允許。
潛在的缺點:
- 在某些情況下,當無法確定原點時,它可能具有* 的值。
- 由于該函數將針對每個請求執行,因此建議使該函數的運行速度盡可能快些。
- 如果將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 (http.Server) 要附加到的服務器
- options (Object)
使用提供的選項(可選)將服務器連接到httpServer上的engine.io實例(可選)。
server.attach(port[, options])
- port (Number) 要監聽的端口
- options (Object)
使用提供的選項(可選)將服務器連接到新的http.Server上的engine.io實例(可選)。
server.listen(httpServer[, options])
server.attach(httpServer [, options])的同義詞。
server.listen(port[, options])
server.attach(port[, options])的同義詞。
server.bind(engine)
- engine (engine.Server)
- Returns Server
僅限高級使用。將服務器綁定到特定的engine.io服務器(或兼容的API)實例。
server.onconnection(socket)
- socket (engine.Socket)
- Returns Server
僅限高級使用。從傳入的engine.io(或兼容的API)套接字創建一個新的socket.io客戶端。
server.of(nsp)
- (nsp (String)
- Returns Namespace
通過路徑名標識符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);
server.engine.generated
覆蓋默認方法以生成您的自定義的socket.id。
該函數使用節點請求對象(http.IncomingMessage)作為第一個參數進行調用。
io.engine.generateId = (req) => {
return "custom:id:" + custom_id++; // custom id must be unique
}
命名空間
表示在由路徑名標識的給定范圍下連接的socket池(例如:/ chat)。
客戶端總是連接到/(主命名空間),然后可能連接到其他命名空間(同時使用相同的底層連接)。
namespace.name
- (String)
名稱空間標識符屬性。
namespace.connected
- (Object<Socket>)
連接到此名稱空間的Socket對象的哈希值,由id索引。
namespace.adapter
- (Adapter)
用于命名空間的適配器。使用基于Redis的適配器時很有用,因為它提供了管理集群socket和rooms的方法。
注意:主名稱空間的適配器可以通過io.of('/').adapter進行訪問。
namespace.to(room)
- room (String)
- Returns Namespace for chaining
為后續事件發送設置一個修飾符,該事件將只廣播給已加入給定房間的客戶端。
要發送到多個房間,您可以多次調用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.clients(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]
});
namespace.use(fn)
- fn (Function)
注冊一個中間件,每傳入一個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'的同義詞
標志:'volatile'
為后續事件發射設置一個修飾符,如果客戶端不準備接收消息,則可能會丟失事件數據(由于網絡緩慢或其他問題,或者因為它們通過長輪詢連接并且處于請求-響應周期中)。
io.volatile.emit('an event', { some: 'data' }); //客戶端可能也可能接收不到信息
標志:'local'
為后續事件發射設置一個修飾符,該事件數據只會廣播到當前節點(使用Redis適配器時)。
Socket
Socket是與瀏覽器客戶端交互的基礎類。一個Socket屬于某個命名空間(默認為/),并使用一個底層客戶端進行通信。
應該注意的是,Socket并不直接與實際的底層TCP/IP socket相關,而只是該類的名稱。
在每個名字空間內,你還可以定義Socket可以加入和離開的任意通道(稱為空間)。這提供了一種方便的方式來廣播到一組socket(請參閱下面的Socket)。
Socket類繼承自EventEmitter。 Socket類覆蓋emit方法,并且不修改任何其他EventEmitter方法。此處記錄的所有方法也為由EventEmitter實現的方法(除emit之外),并且適用于EventEmitter的文檔。
socket.id
- (String)
會話的唯一標識符,來自底層客戶端。
socket.rooms
- (Object)
標識客戶所在房間的字符串散列,按房間名稱索引。
io.on('connection', (socket) => {
socket.join('room 237', () => {
let rooms = Object.keys(socket.rooms);
console.log(rooms); // [ <socket.id>, 'room 237' ]
});
});
socket.client
- (Client)
底層Client對象的引用。
socket.conn
- (engine.Socket)
對底層客戶端傳輸連接(engine.io Socket對象)的引用。這允許訪問IO傳輸層,大多數情況它仍然是抽象出的TCP/IP socket連接。
socket.request
- (Request)
一個getter代理,用于返回發起底層engine.io客戶端的請求的引用。用于訪問Cookie或User-Agent等請求標頭。
socket.handshake
*(Object)
握手細節:
{
headers: /* 握手部分的頭 */,
time: /* 創建時間 (字符串) */,
address: /* 客戶端ip地址 */,
xdomain: /* 連接是否跨域 */,
secure: /* 連接是否安全 */,
issued: /* 創建數據 (unix 時間戳) */,
url: /* 請求url地址 */,
query: /* 詢問對象 */
}
用法:
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)
- 送消息事件。請參閱socket.emit(eventName [,... args] [,ack])。
socket.emit(eventName[, ...args][, ack])
(overrides EventEmitter.emit)
- eventName (String)
- args
- ack (Function)
- Returns 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)
(inherited from EventEmitter)
- eventName (String)
- callback (Function)
- Returns 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.removeAllListeners([eventName])
socket.eventNames()
繼承自EventEmitter(以及此處未提及的其他方法)。請參閱events模塊的Node.js文檔。
socket.join(room[, callback])
- room (String)
- callback (Function)
- Returns Socket for chaining
將客戶端添加到房間,并可選擇觸發具有err簽名的回調(如果有的話)。
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'); // 廣播到房間中的每個人
});
});
連接房間的機制由已配置的適配器處理(請參閱上面的Server#adapter),默認為socket.io-adapter。
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.join(rooms[, callback])
- rooms (Array)
- callback (Function)
- Returns Socket for chaining
將客戶端添加到房間列表中,并可選擇觸發帶有err簽名的回調(如果有)。
socket.leave(room[, callback])
- room (String)
- callback (Function)
- Returns Socket for chaining
從房間中刪除客戶端,并可選擇觸發err簽名(如果有)。
斷開后會自動離開房間
socket.to(room)
- room (String)
為后續事件發射設置一個修飾符,該事件將僅播放給已加入給定房間的客戶端(socket本身被排除)。
要發送到多個房間,您可以多次調用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');
});
注意:廣播時不支持確認。
socket.in(room)
Synonym of socket.to(room).
socket.compress(value)
- value (Boolean) whether to following packet will be compressed
- Returns Socket for chaining
為后續事件發射設置修飾符,該事件數據只有在值為true時才會被壓縮。當您不調用該方法時,默認為true。
io.on('connection', (socket) => {
socket.compress(false).emit('uncompressed', "that's rough");
});
socket.disconnect(close)
- close (Boolean) whether to close the underlying connection
- Returns Socket
斷開這個客戶端。如果close值為true,則關閉底層連接。否則,它只是斷開命名空間。
io.on('connection', (socket) => {
setTimeout(() => socket.disconnect(true), 5000);
});
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 (String) 斷開連接的原因 (客戶端或服務器端)
當斷開連接時觸發
io.on('connection', (socket) => {
socket.on('disconnect', (reason) => {
// ...
});
});
Event: 'error'
- error (Object) error object
當錯誤發生時觸發
io.on('connection', (socket) => {
socket.on('error', (error) => {
// ...
});
});
Event: 'disconnecting'
- reason(String)斷開原因(客戶端或服務器端)
當客戶端斷開連接時(但尚未離開客房)會被觸發。
io.on('connection', (socket) => {
socket.on('disconnecting', (reason) => {
let rooms = Object.keys(socket.rooms);
// ...
});
});
這些是不能用作事件名稱的保留事件(例如connect,newListener和removeListener)。
Client
客戶端類表示傳入的傳輸(engine.io)連接。客戶端可以與許多屬于不同命名空間的多路復用socket相關聯。
client.conn
- (engine.Socket)
引用底層的engine.io Socket連接。
client.request
- (Request)
一個getter代理,用于將引用返回給由engine.io連接而發起的請求。用于訪問Cookie或User-Agent等請求頭。
由于我也是第一次使用socket.io,其中也肯定有許多不準確的地方,如果發現的話,歡迎留言告知
現在基本上只是找個英文文檔原模原樣的翻譯,后面打算自己寫一個socket應用,再寫一份自己的日志