一、php安装扩展组件Swoole
参考连接
二、配置linux服务器
在站点的配置文件中#SSL-END下面添加代码
location /wss/ {
#通过配置端口指向部署websocker的项目
proxy_pass http://127.0.0.1:9205/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
注意:
1、端口号不能与同服务器的wss一样
2、 开放对应的端口(9205)
三、配置微信小程序socket合法域名
注意:后面不需要加端口 ,在站点的配置文件中已经做了定向
四、服务器命令执行wss.php文件(以下命令请在wss.php文件对应的目录执行)
测试启动:php wss.php
设置常驻:nohup php -f wss.php
执行完成后,在对应的文件目录下面会生成一个nohup.out记录文件(不要删除这个文件)
查看进程:netstat -ntlp
结束进程:kill 进程编号
注意:上线一定要执行常驻命令,不执行会报错。因为终端关闭后就无法建立连接。
终端:
nohup.out文件:
五、微信小程序创建连接
在小程序全局app.js初始化时执行创建连接
var util = require("pages/utils/util.js");
var websocket = require("pages/utils/websocket.js");
//app.js
App({
//全局变量
globalData: {
swook_url: "wss://*********/wssc/",
//webSocket
WebSocketSendMsgCallback: function () {},
},
/**
* 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
*/
onLaunch: function () {
var that = this;
//心跳设置
var userInfo = wx.getStorageSync('userInfo') || {};
if (userInfo.id) { //必须在登录状态下才可以连接
that.linkWebsocket(userInfo);
}
},
/**
* 当小程序从前台进入后台,会触发 onHide
*/
onHide: function () {
var that = this;
},
/**
* 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
*/
onError: function (msg) {
},
//开启WebSocket
linkWebsocket: function (userInfo) {
var that = this;
wx.connectSocket({
url: that.globalData.swook_url,
success(res) {
console.log('连接成功~')
wx.onSocketOpen(() => {
console.log('WebSocket连接打开');
//第一次创建连接成功时获取分配的$fd存入数据库,绑定用户
wx.sendSocketMessage({
data: JSON.stringify({
pages: 'A_member',
act: 'initSocket',
member_id: userInfo.id,
content: 'initSocket',
})
})
//数据返回处理
wx.onSocketMessage((res) => {
var objData = JSON.parse(res.data);
if (objData.code == "600") {
//处理数据
that.globalData.WebSocketSendMsgCallback(objData);
}
})
//连接成功打开,拉起心跳检测
websocket.linkWebsocket_xin(40000, true);
})
},
});
wx.onSocketError(function (res) {
console.log('打开失败~,请联系管理员');
});
}
})
pages\utils\websocket.js
注意:发送信息时也算是一次心跳,所以在发送信息之前把当前的心跳关闭,发送成功之后再以发送成功时的时间再次建立心跳连接。
例如:我在10:00:00发送的心跳连接,下次的心跳应该是10:00:40。但是我在10:00:10时发送了一条信息,那么下次的心跳连接时间应该是10:00:50
//心跳重连检测
function linkWebsocket_xin(lian_time, mag) {
if (mag == true) {
var dingshi_title = '';
dingshi_title = setInterval(function () {
console.log("当前心跳已重新连接");
//循环执行代码
wx.sendSocketMessage({
data: JSON.stringify({
pages: 'A_member',
act: 'ping',
}),
fail(res) {
// console.log(res)
}
});
}, lian_time) //循环时间,注意不要超过1分钟
wx.setStorageSync('dingshi_title', dingshi_title);
} else {
var dingshi_title = wx.getStorageSync('dingshi_title') ? wx.getStorageSync('dingshi_title') : '';
//关闭定时器
clearInterval(dingshi_title);
console.log("当前心跳已关闭");
}
}
//发送消息
function send(msg) {
//关闭定时器
linkWebsocket_xin(40000, false);
wx.sendSocketMessage({
data: msg,
success(res) {
//重置定时器时间
linkWebsocket_xin(40000, true);
},
fail(res) {
// console.log(res)
}
});
}
module.exports = {
send: send,
linkWebsocket_xin: linkWebsocket_xin,
}
六、发送消息,推送原理
例如在一个聊天室中存在5个人,那么这个聊天室会绑定这5个人的会员ID,利用会员ID在获取创建swook连接时分配的$fd,在分别进行通知。通知时需验证当前的$fd是否有效。
那么通知内容应该是什么呢?
我做的是,发送消息成功存入数据库后对小程序进行返回时,进行通知
例如:
//注意引入websocket.js
//内容进程提交
websocket.send(
JSON.stringify({
pages: 'A_member',
act: 'add',
detailId: that.data.detailId,//当前聊天室ID
message: data.fan_arr,//发送的内容
}),
);
七、wss.php文件
面向过程和面向对象的写法官方给了哈 <<点击进入
server = new Swoole\WebSocket\Server("0.0.0.0", 9205);
$this->server->on('open', function (swoole_websocket_server $server, $request) {//用于指定连接成功后的回调函数
echo date("Y-m-d H:i:s") . ":用户 fd{$request->fd} 成功链接\n";
});
$this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {//用于指定收到服务器数据后的回调函数
// echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
// $server->push($frame->fd, "this is server");
// //引用
$conn = _conn();
$infoArray = array();
$infoArray["code"] = 0;
$infoArray["msg"] = "REQUEST FAILED";
$webSocket_id = $frame->fd;
$webSocket_data = $frame->data;
$webSocket_data = htmlspecialchars_decode($webSocket_data);
$webSocket_data = $webSocket_data ? json_decode($webSocket_data, true) : array();
if(!is_array($webSocket_data)){
$webSocket_data = array();
}
$pages = $webSocket_data["pages"];
$act = $webSocket_data["act"];
$detailId = (int)$webSocket_data["detailId"];
$member_id = (int)$webSocket_data["member_id"];
$message = $webSocket_data["message"];
//log
echo date("Y-m-d H:i:s") . ":用户 fd{$frame->fd} 发送消息 {$pages},{$act},{$detailId}\n";
if($pages == "A_member"){
if($act=="initSocket"){
//首次握手用户绑定
_wss_member_fid($this->server, $conn, $webSocket_id, $member_id);
$infoArray["code"] = 200;
$infoArray["msg"] = "initSocket success";
}elseif($act=='add'){//发送消息返回
// $infoArray["code"] = 200;
// $infoArray["msg"] = "OK";
include ("A_chat.php");
}elseif($act=='ping'){//心跳连接
$infoArray["code"] = 666;
$infoArray["msg"] = "OK_xin";
$infoArray["content"] = "pong";
}
}
//发送
$json = json_encode($infoArray, true);
$server->push($webSocket_id, $json);
});
$this->server->on('close', function ($ser, $webSocket_id) {//用于指定连接关闭后的回调函数
//echo "client {$fd} closed\n";
//引用
$infoArray["code"] = 200;
$infoArray["msg"] = "initSocket success";
$infoArray["webSocket_id"] = $webSocket_id;
$conn = _conn();
//获取
if($webSocket_id){
$sqlrm = "select * from a_member where webSocket_id='".$webSocket_id."' ";
$resultrm = mysqli_query($conn,$sqlrm);
$rm = mysqli_fetch_array($resultrm);
//log
echo date("Y-m-d H:i:s") . ":用户 fd{$webSocket_id} 关闭链接\n";
//清除状态
$sqlEdit = "update a_member set webSocket_id=0 where webSocket_id={$rm["webSocket_id"]}";
$resultEdit = mysqli_query($conn,$sqlEdit);
}
});
$this->server->on('request', function ($request, $response) {
// 接收http请求
echo "request\n";
echo json_encode($request)."\n";
echo json_encode($response)."\n";
echo $request->get['message']."\n";
// // 接收http请求从get获取message参数的值,给用户推送
// // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
// foreach ($this->server->connections as $fd) {
// // 需要先判断是否是正确的websocket连接,否则有可能会push失败
// if ($this->server->isEstablished($fd)) {
// $this->server->push($fd, $request->get['message']);
// }
// }
});
$this->server->start();
}
}
new WebsocketTest();
?>
八、发送消息返回
WebSocketSendMsgCallback,这个是实时返回。
wx.onSocketMessage((res) => {
var objData = JSON.parse(res.data);
if (objData.code == "600") {
//处理数据
that.globalData.WebSocketSendMsgCallback(objData);
}
})
接收数据:
app.globalData.WebSocketSendMsgCallback = function (data) {
if (data.act == "chat_room_list" && data.pages == "A_chat") {
var subjects = that.data.subjects;
var message = data.message;
if (data.member_id == userInfo.id) {
message[0].admin = 1;
} else {
message[0].admin = 2;
}
subjects = subjects.concat(message);
that.setData({
subjects,
})
}
}
九、日志查看
另外还有就是如果服务器重启了,需要服务器再次执行wss.php文件
当会员退出登录时,那么当前的连接应该断了wx.closeSocket();
wss.php文件中有监听断开的处理,应该是把绑定会员的fd清除。
如果会员登录时,那么应该在次建立连接