如何在小程序中实现 WebSocket 通信

字数统计: 4.3k阅读时长: 17 min
2018/11/14 Share

在以前的文章中,我们介绍了HTTP通讯,这种通讯有一个缺点,如果我想从直接从服务器发消息给客户端,需要客户端先发起HTTP请求后服务器才能返回数据,且后续服务器想发送数据给客户端都需要客户端先发起请求,但这种方案在一些特殊场景应用的时候非常消耗资源,比如聊天室,如果使用HTTP请求,需要客户端每隔一段时间就请求一次服务器,再由服务器返回数据。这种传统的模式带来很明显的缺点,即客户端需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。WebSocket只需要与服务器进行一次握手,即可实现实时的数据连接,并且传输协议是全双工的,服务器可以随时主动向客户端发送数据,并且WebSocket协议在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小,能明显降低服务器及客户端开销。

我们的小程序也支持WebSocket通信,如果你想为你的小程序实现聊天室、服务器推送、小程序之间数据交互等功能,那就非常有必要搭建一个WebSocket服务器来进行WebSocket通讯。这篇文章中,我们将简单介绍小程序WebSocket通信使用方法,并通过实例搭建一个WebSocket服务器。实现小程序与服务器之间的通讯。

在教程开始之前,需要搭建搭建好小程序的基础开发环境,关于如何配置,大家可以参考如何入门小程序开发这篇文章的入门教程。

服务器搭建

既然要实现WebSocket通讯,那必须要拥有一台WebSocket服务器,服务端的环境有很多选择NodeJS、PHP、Python等大部分主流语言都可以部署WebSocket服务,今天我们将教大家使用PHP语言进行环境部署,其他语言请同学们自行部署。

运行环境搭建

我这里以Ubuntu Server 16.04 LTS为例,我们需要安装php运行环境及NginxWeb服务,同时也需要申请免费的SSL证书和域名,关于证书和域名的申请注册请参考如何快速搭建微信小程序这篇文章。注册完域名及证书申请,我们就可以开始部署服务器了!首先,登录服务器,执行下面的命令。

1
2
sudo apt update
sudo apt install php php-fpm php-curl nginx composer -y

安装完成后,使用浏览器访问你的服务器IP地址,如果看到下面的内容,则证明Web服务已经启动。

img

因为小程序获取远程数据,必须为HTTPS或WSS环境,所以目前搭建的环境,在小程序无法使用,接下来,我们将使用SSL证书加密小程序访问你服务器之间的流量。这里就需要刚才注册的域名及证书了。首先,将下载的证书,上传到你的服务器,并记录下这个位置。然后,我们将配置Nginx服务,以让其支持WSS流量。

我们找到/etc/nginx/conf.d文件夹,新建配置文件,为了方便后续修改,我将这里的配置文件修改为weixin.techeek.cn.conf大家可以根据自己的需求修改。

1
2
cd /etc/nginx/conf.d
sudo nano weixin.techeek.cn.conf

nano编辑器中,我们写下下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
server {
listen 443 ssl;
server_name weixin.techeek.cn;
index index.php index.html index.htm;
root /usr/share/nginx/html;
ssl_certificate /home/ubuntu/1_weixin.techeek.cn_bundle.crt;
ssl_certificate_key /home/ubuntu/2_weixin.techeek.cn.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location ~ .php$ {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location /
{
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Real-IP $remote_addr;
}
}

server {
listen 80 default_server;
server_name weixin.techeek.cn;
root /usr/share/nginx/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}

一定注意,将文中server_name中的weixin.techeek.cn更换成你的域名。将ssl_certificatessl_certificate_key中证书的路径更换成你刚上传证书的路径。然后,执行下面的命令重启nginx服务。

1
sudo service nginx restart

之后,打开你电脑的浏览器,然后通过域名访问,注意,这里一定要在域名前加https://,比如我访问的域名https://weixin.techeek.cn/

1542188355313

如果域名前有小锁标志,则证明你已经配置成功,可以开始下一步了,这里502报错不用在意,因为我们还没有搭建WebSocket服务,所以服务器会返回502错误。

配置通讯域名

基本环境配置好之后,可以登录 微信公众平台 配置通信域名了。我们点击微信公众号右侧的设置,然后找到服务器域名配置。

1542188610710

进入微信公众平台管理后台设置服务器配置,如上图所示,需要将你的服务器域名配置为你自己的域名。我这里的域名是weixin.techeek.cn

WebSocket服务搭建

上述步骤准备完成后 ,就可以撰写WebSocket服务端的代码了,我这里使用的是PHP socket即时通讯框架Workerman来进行搭建。有了这个框架,我们就可以非常方便的搭建WebSocket服务。因为本文主要讲解小程序端的WebSocket的使用,关于Workerman的详细使用教程,可以参考Workerman官方手册,本文仅做基础环境安装的介绍。

首先,我们创建一个运行WebSocket服务的目录,我这里创建名为php-websocket-server,目录位置可以自定义,我这里就将项目放在ubuntu用户的根目录下。

1
2
mkdir /home/ubuntu/php-websocket-server
cd /home/ubuntu/php-websocket-server

接下来,我们使用composer包管理器安装WebSocket运行环境。因为某些原因,国内访问composer可能会报错,所以我们需要使用国内的composer镜像。然后就可以安装Workerman了。

1
2
3
sudo composer config -g repo.packagist composer https://packagist.phpcomposer.com
sudo composer update
sudo composer require workerman/workerman

安装完成后,默认情况下会有三个文件,composer.jsoncomposer.lockvendor这三个文件,如果没有,请重新执行上面的命令。

1
2
3
├── composer.json
├── composer.lock
└── vendor

安装完workerman依赖文件,我们就可以撰写系统所需的代码了。使用nano编辑器,新建一个可执行的php文件,我这里创建的文件名为webSocket.php,大家可自行更改。

1
nano webSocket.php

代码如下

1
2
3
4
5
6
7
8
9
10
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$ws_worker = new Worker("websocket://0.0.0.0:8080");
$ws_worker->count = 4;
$ws_worker->onMessage = function($connection, $data)
{
$connection->send('hello ' . $data);
};
Worker::runAll();

这时,一个最基本的websocket服务就编辑完成了,这里的代码意思是,通过/vendor/autoload.php引入Workerman的php文件,然后在8080端口创建websocket服务,并设置进程为4个进程。然后执行onMessage回调函数,该函数接收客户端所发过来的数据$data,然后使用send方法将数据发回给客户端。

接下来,我们就可以运行服务器了,执行下面的代码即可运行。

1
sudo php webSocket.php start

如果看到类似下面的输出,证明我们websocket服务器已经启动,接下来就可以开始配置小程序端的代码了。

1542247109151

小程序端

连接服务器

小程序连接Websocket服务器是通过wx.connectSocket()API进行连接的,为了方便连接API,我们先看看官方的文档

属性 类型 是否必填 说明
url string 开发者服务器 wss 接口地址
header Object HTTP Header,Header 中不能设置 Referer
protocols Array. 子协议数组
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

我们看到只有url是必填项,其他属性可以不填,那么连接服务器就比较简单了,我们打开index.js文件,写下下面的代码。

1
2
3
4
5
6
7
Page({
onReady: function () {
wx.connectSocket({
url: 'wss://weixin.techeek.cn'
})
},
})

有小程序开发经验的小伙伴都知道,这里的onReady是小程序的生命周期函数,负责在小程序初次渲染完成后执行的函数,这样我们编译完小程序,小程序就自动连接服务器。现在编译一下试试,咦,好像不行啊,怎么没看到小程序有反应。我们打开控制台,点击Network按钮,如果看到类似下面的内容,就证明你的小程序已经成功链接服务器了。

1542249696427

这里的HTTP状态码是101,101状态码是websocket特有的状态码,我们已经成功连接搭建的服务器。但是我们能不能直观点看到已经连接服务器呢?当然可以,参考文档使用success属性,我们在其中加入回调函数。修改代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
Page({
onReady: function () {
wx.connectSocket({
url: 'wss://weixin.techeek.cn',
success: function (res) {
console.log("连接服务器成功")
},
fail: function (res) {
console.log("连接服务器失败")
}
})
},
})

我们增加一个回调函数,如果服务器连接成功,向小程序控制台打印出连接服务器成功。反正打印连接服务器失败

1542250247835

当然,我们也可以将成功的内容展示给小程序前端,代码如下,首先修改index.wxml代码。

1
<view><text>连接服务器状态:{{status}}</text></view>

然后打开index.js文件,修改代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Page({
onReady: function () {
var myThis = this;
wx.connectSocket({
url: 'wss://weixin.techeek.cn',
success: function (res) {
myThis.setData({
status: "连接服务器成功"
})
},
fail: function (res) {
myThis.setData({
status: "连接服务器失败"
})
}
})
},
})

现在重新编译小程序,你会看到类似这样的界面。

1542250497626

向服务器发送数据

服务器搭建我们说到,我们的服务器的代码内容是将小程序发给服务器的任意字符前加hello之后返回给小程序,现在,我们已经成功连接服务器了。接下来,我们需要修改代码,以便小程序将数据发给服务器。

官方文档中,使用wx.sendSocketMessage()API将数据发给服务器,根据官方文档,通过 WebSocket 连接发送数据。需要先wx.connectSocket连接服务器,并在 wx.onSocketOpen 回调之后才能发送。所以在调用wx.sendSocketMessage()前,需要先调用wx.onSocketOpen监听WebSocket连接是否打开。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
Page({
onReady: function () {
var myThis = this;
wx.connectSocket({
url: 'wss://weixin.techeek.cn'
})
wx.onSocketOpen(function (res) {
myThis.setData({
status: "websocket连接服务器成功"
})
})
},
})

现在,我们就可以使用wx.sendSocketMessage()发送数据到服务器了,先看看官方文档,怎么使用。

属性 类型 是否必填 说明
data string/ArrayBuffer 需要发送的内容
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

我们只需要传data内容给API,就能发内容给服务器了,那么修代码内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Page({
onReady: function () {
var myThis = this;
wx.connectSocket({
url: 'wss://weixin.techeek.cn'
})
wx.onSocketOpen(function (res) {
wx.sendSocketMessage({
data: "你好",
success: function (res) {
console.log("数据已发给服务器")
}
})
myThis.setData({
status: "websocket连接服务器成功"
})
})
},
})

现在,我们的数据已经可以发给服务器了,可是我们还没有看到服务器返回的数据,这时,我们就该使用另一个API了,监听WebSocket 接受到服务器的消息事件wx.onSocketMessage(),该API返回服务器发出的消息。但是onReady函数是页面加载就运行的,这时服务器还没反应过来,数据返回了没收到该怎么处理?我们可以引入另一个生命周期函数onLoad,这个函数是小程序负责监听页面加载的函数,我们可以将服务器消息事件监听的API写在这里,当接收到数据,由这个函数返回相关内容。所以代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Page({
onReady: function () {
var myThis = this;
wx.connectSocket({
url: 'wss://weixin.techeek.cn'
})
wx.onSocketOpen(function (res) {
wx.sendSocketMessage({
data: "你好",
success: function (res) {
console.log("数据已发给服务器")
}
})
myThis.setData({
status: "websocket连接服务器成功"
})
})
},
onLoad: function (options) {
var myThis = this;
wx.onSocketMessage(function (res) {
myThis.setData({
message: res.data
})
})
},
})

为了方便观察服务器返回的数据,我们修改下前端,增加服务器消息监听的内容。

1
2
<view><text>连接服务器状态:{{status}}</text></view>
<view><text>服务器消息:{{message}}</text></view>

现在,重新编译,就能看到服务器返回Hello 你好的内容,我们发出的内容为你好,服务器在内容前加一个Hello然后返回给小程序。我们可以修改你好为任意内容,看看服务器能否正常返回相关内容。稍微优化下前端和后端代码,如下。

index.wxml

1
2
3
4
5
6
<button type="primary" bindtap="connect">连接webSocket服务器</button>
<button type="warn" bindtap="close">断开webSocket服务器</button>
<input placeholder="在这里输入你要发送的弹幕内容" bindblur="input"/>
<button bindtap="send">向webSocket服务器发送消息</button>
<view><text>连接服务器状态:{{status}}</text></view>
<view><text>服务器消息:{{message}}</text></view>

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Page({
connect() {
var myThis = this;
wx.connectSocket({
url: 'wss://weixin.techeek.cn'
})
wx.onSocketOpen(function (res) {
myThis.setData({
status:"websocket连接服务器成功"
})
})
},
close(){
var myThis = this;
wx.closeSocket()
wx.onSocketClose(function (res) {
myThis.setData({
status: "websocket服务器已经断开"
})
})
},
send(){
var myThis = this;
wx.sendSocketMessage({
data: this.inputValue,
success: function (res) {
console.log("发送信息")
wx.showToast({
title: '已发送',
icon: 'success',
duration: 1000
})
},
fail: function (res) {
myThis.setData({
status: "请连接服务器"
})
}
})
},
input: function (e) {
this.inputValue = e.detail.value
},
onLoad: function (options) {
var myThis = this;
wx.onSocketMessage(function (res) {
myThis.setData({
message:res.data
})
wx.showToast({
title: '你收到来自服务器的消息',
icon: 'none',
duration: 2000
})
})
},
})

1542253679047

这样,我们就实现了向服务器发送数据,同时服务器返回数据的全部流程。

服务器主动发送数据到小程序

有人可能会问,这个HTTP通信方式没有区别啊,还是小程序先请求数据到服务器,然后服务器返回数据啊,我没看到什么不同。虽然表现是这样,但是现在小程序和服务器是长连接状态,服务器可以直接推送内容到小程序,不信?我们试试。打开你的服务器Websocket.php文件,将代码修改为下面的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Lib\Timer;

$worker = new Worker('websocket://0.0.0.0:8080');
$worker->onWorkerStart = function($worker){
Timer::add(10, function()use($worker){
foreach($worker->connections as $connection) {
$connection->send('你好!');
}
});
};

$worker->onMessage = function($connection, $data)
{
echo $data . "\n";
$connection->send('服务器已经收到了你的消息');
};
Worker::runAll();

然后运行服务器。

1
sudo php webSocket.php start

这行代码中,我们实现了小程序连接服务器后,服务器每隔10秒主动推送数据你好给小程序,无需小程序主动请求内容,同时,小程序发出的内容,可以在服务端显示。现在点击你小程序连接webSocket服务器按钮,看看效果。

1542254161118

然后我们向服务器发点消息试试。服务器也已经收到了小程序发出的数据。

1542254236499

总结

websocket通信在小程序端还是比较简单的,赶快去自己试试吧~后续我还会介绍一篇利用websocket通讯进行聊天室搭建的教程,喜欢的小伙伴请持续关注本专栏。腾讯云联合小程序给大家带来了小程序·云开发解决方案,为开发者提供完整的云端支持,弱化后端和运维操作,使用平台原生 API 进行核心业务开发,实现快速上线和迭代。欢迎免费使用!

原文作者:张诚

原文链接:https://www.techeek.cn/wx-websocket

发表日期:November 14th 2018, 3:33:57 pm

更新日期:November 16th 2018, 10:27:33 am

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可,转载请注明出处。

CATALOG
  1. 1. 服务器搭建
    1. 1.1. 运行环境搭建
    2. 1.2. 配置通讯域名
    3. 1.3. WebSocket服务搭建
  2. 2. 小程序端
    1. 2.1. 连接服务器
    2. 2.2. 向服务器发送数据
    3. 2.3. 服务器主动发送数据到小程序
  3. 3. 总结