Table of Contents
有这么一个场景:服务器需要经过较长时间才能得出结果,网页需要知道服务器的处理进度,避免用户误以为页面卡死,也让用户知道操作需要多长时间完成。
实现这个功能需要依靠 WebSocket。网页操作(点击按钮)后,打开 web-socket 连接,发送参数。服务器开始计算,并每隔一段时间发送处理进度。最后网页得到结果后,关闭连接。
网页端的 api 这里就不记录了,直接 mdn 文档查看就是。没太多意外。但服务端就不同了。我用的 nodejs ,但 nodejs 也没像网页端一样提供开箱即用的 api。
本来是准备看看 socket.io 的,但这个需要监听额外的端口。后来发现了 express-ws。这个库是给 express 添加 ws 能力的,端口复用,用法和 express 差不多。
安装编码
首先,安装 express-ws:
yarn add express-ws
然后 app.js 里引入。根据 github 的文档说明,express-ws 要在 router 引入之前挂载到 app 上,否则会报错:router.ws is not a function。
var express = require('express'); var app = express(); // 需要在加载 router 之前,否则会报错:router.ws is not a function // https://www.npmjs.com/package/express-ws var expressWs = require('express-ws')(app, server) var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users');
接着,在 routes/index.js 里写接口。
/** * websocket */ router.ws('/query-batch', function (ws, req) { ws.on('message', function (msg) { ws.send('hello') }) ws.on('close', function(code, reason) { // do something }) })
这写法和 express 其他路由的写法简直一模一样。
404
好了,web 可以发起 WebSocket 请求了。然后……404 了。
???
我把 app.js 和 router.ws() 部分代码检查了好几遍,确定没有问题,确定和官方 demo 一样——express-ws 在 router 之前。
难道要写一个同样路径的 get 路由?毕竟 WebSocket 是基于 http 的,并且 firefox 的报错也告诉我 get 404 not found。添加好 get 路由后,不 404 了,但 WebSocket 还是没有成功。
😊(保持微笑)
然后,我把官方的示例下载下来,启动,测试,一气呵成,no problem。???
好吧,最起码证明这个包是有用的。
几经折腾之后,我终于在 StackOverflow 找到了解决方法:
// app.js var app = express(); var server = require('http').Server(app); var expressWs = require('express-ws')(app,server); ... ... //module.exports = app; module.exports = {app: app,server: server};
// bin/www //var app = require('../app'); var app = require('../app').app; ... ... //var server = http.createServer(app); var server = require('../app').server;
改完后,web 发起 WebSocket 请求,连接成功。
总算好了,不过最后还是没用 WebSocket,因为 WebSocket 的数据压缩是个问题。普通请求返回 json 数据直接通过 nginx gzip 压缩就是了,WebSocket 就无能为力了。也许可以解决,但绝对没有配置 nginx 简单。