通过 WebSocket 实现 web-socket 通信

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 简单。