WebSocket 以及 socket.io 使用
说明
WebSocket 是一个持久化的协议, 是基于HTTP协议的, 它支持长连接,而不是像 ajax 一样通过轮询,每隔一段时间,向服务器发送请求,询问是否有新的信息,服务器端不能主动联系客户端,只能由客户端发起。对于 WebSocket 它只需要一次对服务器的请求,然后服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息,而且跨域通信。
WebSocket API
不同服务器端语言有不同的 API, 一下主要展示客户端API
1. 创建 WebSocket 实例
const w = new WebSocket('ws://localhost:8181');
‘ws://localhost:8181’ 表示要连接的URL。这个URL应该为响应WebSocket的地址, ws 表示WebSocket协议。
使用“Echo”服务器:ws://echo.websocket.org 能将发送过去的数据返回, 可以用于测试能否链接上
2. 与服务器建立连接后的回调
w.onopen = function(){}
成功建立连接w.send()
向服务器发送数据w.close()
关闭 WebSocket 链接或停止正在进行的链接请求
w.onopen = function(a) {
/*
一旦服务器响应WebSocket 链接请求就会触发open 事件
open事件触发,意味着握手协议结束,WebSocket已经准备好收发数据,
如果你的应用收到open事件,就可以确定服务端已经处理了建立连接的请求,且同意和你的应用通信。
*/
console.log('Connect open...');
console.log(w);
/*
发送一个文本消息,在open之后,或者根据WebSocket对象的状态判断是否open
*/
w.send('hello websocket');
if (w.readyState === WebSocket.OPEN) {
// 状态码 等于 OPEN 的时候
w.send('is in open');
}
// 状态码
for (let attr in WebSocket) {
console.log(attr, WebSocket[attr]);
/*
CONNECTING 0
OPEN 1
CLOSING 2
CLOSED 3
*/
}
}
3. 接受服务器消息的回调
w.onmessage = function(){}
w.onmessage = function (e) {
/*
接收服务器消息触发的事件
*/
console.log(e.data); // e.data 服务器返回的数据
console.log(w);
/*
close()用于关闭链接,调用后将不能发送数据
*/
w.close();
}
4. 连接失败时触发回调
w.onerror = function(){}
w.onerror = function (e) {
/*
失败时触发,错误会导致链接关闭
*/
console.log(e);
}
5. 关闭链接时回调
w.onclose = function(){}
w.onclose = function (e) {
/*
链接关闭时触发
*/
console.log('close', e);
}
Socket.io API
Socket.io是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架,它除了支持WebSocket通讯协议外,还支持许多种轮询机制以及其它实时通信方式,并封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。会根据浏览器对通讯机制的支持情况,选择最佳方式实现网络实时通信
1. 安装到项目
npm install --save-dev socket.io
2. 服务器端
1. 创建服务
const app = require('http').createServer(handler);
const io = require('socket.io')(app);
const fs = require('fs');
const ip = require('ip').address();
const DB = require('./db.json');
app.listen(8089, ip);
function handler (req, res) {
fs.readFile(__dirname + '../app/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
// 客户端就可以通过 `http://${ip}:8089` 建立 websocket 通信了
2. 监听客户端连接
- socket.on(”, function(data){}) 监听客户端发送的消息
io.on('connection', function (socket) {
/*
监听客户端连接,回调函数会传递本次连接的socket
回调函数的socket 为一个client与服务器的连接标识,不同client不一样
*/
console.log('connection id = ' + socket.id);
socket.on('changeswitch', function (data) {
// 客户端发出的 'changeswitch' 事件的处理函数
console.log(data);
})
})
3. 给客户端发送消息
- socket.emit(”, {}) 在 connection 回调中使用,将数据传输给对应的客户端
- socket.broadcast.emit(”, {}) 信息传输对象为所有client,排除当前socket 对应的 client
- io.socket.emit(”, {}) 信息传输对象为所有client
- io.sockets.socket(socketid).emit(”, {}) 给指定的客户端发送消息
io.on('connection', function (socket) {
socket.on('changeswitch', function (data) {
setTimeout(function () {
/*
socket.emit 信息传输对象为当前socket对应的client,各个client socket 互不影响
socket.broadcast.emit 信息传输对象为所有client,排除当前socket 对应的 client
io.socket.emit 信息传输对象为所有client
*/
io.sockets.emit('changeswitchfinish', {
success: true,
switchInfo: data
});
}, 500);
})
})
3. 客户端
1. 建立 socket 链接
import io from 'socket.io-client'; // 注意要引入 -client 的模块否则报错
import ip from './reuqest'; // ip 是通过 webpack 注入的当前 ip 地址,与配置服务器的地址相同
const socket = io(`http://${ip}:8089`);
socket.on('connect', function () {
console.log('链接成功');
})
2. 监听服务器消息
- socket.on(”, function(data){})
socket.on('connect',function () {
// 连接成功
})
socket.on('connecting',function () {
// 正在连接
})
socket.on('disconnect',function () {
// 断开连接
})
socket.on('connect_failed',function () {
// 连接失败
})
socket.on('error',function () {
// 报错
})
socket.on('reconnect_failed',function () {
// 重连失败
})
socket.on('reconnect',function () {
// 成功重连
})
socket.on('reconnecting',function () {
// 正在重连
})
socket.on('changeswitchfinish', function (data) {
if (data.success) {
dispatch({type: CHANGE_SWITCH, msg: data})
}
})
3. 向服务器发送消息
- socket.emit(”, {}) 使用前要确定与服务器是否链接上
if (socket.connected) {
// statement
socket.emit('changeswitch', {});
}
4. 主动断开链接
- socket.disconnect()
5. 问题
结合 react redux 一起使用遇到了一些问题,选择了一些折中的解决办法
在发出的 action 中使用 WebSocket,由于不能每一个 action 都打开一个 Socket 服务,所以 最后 socket 挂在了 window 上。
这样由于多个 action 都需要进行 WebSocket 通信,所以不能再 socket.on(‘connect’,fn) 函数中进行 socket.emit() 或者 socket.on(”,fn),解决办法是在每次使用(socket.connected)判断是否连接到服务器,然后发送消息
socket.on(”,fn) 自定义的操作都要在初始化建立 socket 链接后紧接着定义,不要单独定义在某一个 action 中。