微信小程序之swoole/WebSocket创建聊天室(php)

Posted 亚历山大海

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信小程序之swoole/WebSocket创建聊天室(php)相关的知识,希望对你有一定的参考价值。

 一、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文件

面向过程和面向对象的写法官方给了哈 <<点击进入

<?php
header("Content-Type: application/json; charset=utf-8");
include 'inc_serve.php';
class WebsocketTest {
    public $server;
    public function __construct() {
        $this->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清除。

如果会员登录时,那么应该在次建立连接

以上是关于微信小程序之swoole/WebSocket创建聊天室(php)的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序token被获取安全吗

微信小程序基础之创建使用教程

微信小程序之组件

微信小程序之项目的创建

微信小程序获取手机号

微信小程序获取手机号