swoole – web百事通 https://www.askme-121.pw web互联网之家 Mon, 29 Jan 2024 06:26:00 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.5.3 https://www.askme-121.pw/wp-content/uploads/2023/12/cropped-05ee702f-4b38-40f3-915f-c8fc68b10a91-32x32.png swoole – web百事通 https://www.askme-121.pw 32 32 静态化API是什么?用Swoole如何去实现呢? https://www.askme-121.pw/static-api/ https://www.askme-121.pw/static-api/#respond Mon, 29 Jan 2024 06:25:32 +0000 https://www.askme-121.pw/?p=503 什么是静态化API?
静态化API可以理解成把一些接口的数据存储在服务器本地。常用的是存成json文件,也可以是放在swoole的table中,总之是用户不从数据库直接读取数据,而是从本地加载的方式来大幅提高性能,因为很多系统的性能瓶颈是在数据库的位置。

解决方案
方案1 easySwoole + crontab
方案2 easySwoole定时器
方案3 Swoole table
方案4 Redis

实现
这里做的分页的场景,不包含分页的源码,只从拿到了分页的数据看定时生成json和获取json的部分

原始的方法 – 读取mysql

这是原始的方法,每个用户访问都会去数据库里面读取一次,每一次分页也会访问数据库,会造成大量的资源开销。

public function lists0(){
    $condition = [];
    if(!empty($this->params['cat_id'])){
        $condition['cat_id'] = intval($this->params['cat_id']);
    }

    try {
        $videoModel = new VideoModel();
        $data = $videoModel->getVideoData($condition, $this->params['page'], $this->params['size']);
    } catch (\Exception $e) {
        //$e->getMessage();
        return $this->writeJson(Status::CODE_BAD_REQUEST,"服务异常");
    }

    if(!empty($data['lists'])){
        foreach ($data['lists'] as &$list){
            $list['create_time'] = date("Ymd H:i:s",$list['create_time']);
            $list['video_duration'] = gmstrftime("%H:%M:%S",$list["video_duration"]);
        }
    }

    return $this->writeJson(Status::CODE_OK,'OK',$data);
}

知识点:

1 获取以秒为单位的时间长度使用gmstrftime(“%H:%M:%S”,$list[“video_duration”])
2 在做模型的时候写一个基类,把连接数据库的工作放在这个基类的构造方法当中,这样每次实例化的时候就自动连接了,提高代码的复用性

<?php


namespace App\Model;

use EasySwoole\Core\Component\Di;

class Base
{
    public $db = "";

    public function __construct()
    {
        if(empty($this->tableName)){
            throw new \Exception("table error");
        }
        $db = Di::getInstance()->get("MYSQL");
        if($db instanceof \MysqliDb){
            $this->db = $db;
        }else{
            throw new \Exception("db error");
        }
    }

    public function add($data){
        if(empty($data) || !is_array($data)){
            return false;
        }
        return $this->db->insert($this->tableName,$data);
    }

}

方案一、方案二 使用easySwoole定时器以及contab的生成静态化API

这是直接读取静态化API的方法,其实就是读文件然后返回给前端

/**
 * 第二套方法 - 直接读取静态化json数据
 * @return bool
 */
public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json";
    $videoData = is_file($videoFile) ? file_get_contents($videoFile) : [];
    var_dump($videoData);
    $videoData = !empty($videoData) ? json_decode($videoData,true) : [];

    $count = count($videoData);
    // var_dump($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

这里的getPagingData使用了array_slice来切割数组来做分页。

/**
 * 获取分页信息
 * @param $count
 * @param $data
 * @return array
 */
public function getPagingData($count, $data){
    $totalPage = ceil($count / $this->params['size']);
    $data = $data ?? [];
    $data = array_slice($data, $this->params['from'], $this->params['size']);
    return [
        'total_page' => $totalPage,
        'page_size' => $this->params['page'],
        'count' => intval($count),
        'list' => $data
    ];

}

定时生成Json文件代码 – 核心部分 全局事件easySwooleEvent->mainServiceCreate中执行,这样easySwoole启动了之后就会执行,这里使用的原生的crontab,只能够精确到分

$cacheVideoObj = new VideoCache();
// 使用cronTab处理定时任务
// 这里是闭包 要use $cacheVideoObj之后才能获取,实例化不放在function中是为了防止每次都实例化浪费资源
CronTab::getInstance()
    ->addRule("test_bing_crontab", '*/1 * * * *', function () use($cacheVideoObj) {
        $cacheVideoObj->setIndexVideo();
    });

也可以使用easySwoole的定时器来实现也是放在mainServiceCreate中执行,这里的代码一定要注意要注册一个onWorkerStart。
然后指定一个进程去执行这个定时任务注意一下这里闭包里面又有一个闭包,外面的变量要use两次。
一定要注意easwoole定时器的使用,这里的坑比较多一定要注意,优势是swoole的定时器可以到毫秒级而contab只能到分级

// easySwoole自带定时器
$register->add(EventRegister::onWorkerStart,function (\swoole_server $server,$workerId)use($cacheVideoObj){
    //让第一个进程去执行,否则每个进程都会执行
    if($workerId == 0){
        Timer::loop(1000*2,function ()use ($cacheVideoObj){
            $cacheVideoObj->setIndexVideo();
        });
    }
});

方案三 Swoole table的解决方案

swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。性能十分强悍。使用起来有一点像是缓存。这里不再是生成一个json文件去读取了,读取table中数据给前端的方法,注意要use cache这个类,这里直接使用get就可以根据key获取到table中的数据,注意这个是一个单例模式,因此需要getInstance

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json";

    //  第三套方案 table
    $videoData = Cache::getInstance()->get("index_video_data_cat_id".$catId);
    $videoData = !empty($videoData) ? $videoData : [];

    $count = count($videoData);;
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

设置直接set(key,data)就可以了,注意这里的代码不是很严谨,比如要判断一下是不是设置成功了,没有设置成功发短信之类的,也要处理一下空值的场景,这里只是做一个演示。

<?php

namespace App\Lib\Cache;

use App\Model\Video as VideoModel;
use EasySwoole\Config;
use EasySwoole\Core\Component\Cache\Cache;

class Video
{
    public function setIndexVideo()
    {
        $catIds = array_keys(Config::getInstance()->getConf("category"));
        array_unshift($catIds, 0);

        $modelObj = new VideoModel();

        foreach ($catIds as $catId) {
            $condition = [];
            if (!empty($catId)) {
                $condition['cat_id'] = $catId;
            }

            try {
                $data = $modelObj->getVideoCacheData($condition);
            } catch (\Exception $e) {
                // 短信报警
                $data = [];
            }
            if (empty($data)) {

            }
            foreach ($data as &$list) {
                $list['create_time'] = date("Ymd H:i:s", $list["create_time"]);
                $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]);
            }
            // 由于table存在内存所以重启服务器时数据会丢失,要将配置中的PERSISTENT_TIME设置为1进行落盘操作
            Cache::getInstance()->set("index_video_data_cat_id".$catId, $data);
        }
    }
}

注意,一定要去config里面开启

'PERSISTENT_TIME'=>1//如果需要定时数据落地,请设置对应的时间周期,单位为秒

否则会在重启服务的时候没有数据,因为每次启动的时候swoole table会清空,要等到定时去set table的时候才会有数据,因此要开启数据落盘,这样会生成两个文件:

每次启动的时候由于还没有执行定时任务,就会先读取这两个落盘的文件中的数据,防止服务启动时等待table生成造成业务中断。

方案四 Redis

用redis来做数据缓存,每次从缓存里面度读先重写一下set方法,更加严谨一点

/**
 * 重写set方法 处理一下失效时间以及数组转json
 * @param $key
 * @param $value
 * @param $time
 * @return bool|string
 */
public function set($key, $value, $time){
    if(empty($key)){
        return '';
    }
    if(is_array($value)){
        $value = json_encode($value);
    }
    if(!$time){
        return $this->redis->set($key,$value);
    }
    return $this->redis->setex($key, $time, $value);
}

使用起来很简单啦,在之前的代码中

Di::getInstance()->get("REDIS")->set("index_video_data_cat_id".$catId, $data);

然后取出的数据的部分

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    //redis
    $videoData = Di::getInstance()->get("REDIS")->get("index_video_data_cat_id".$catId);
    $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
    $count = count($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}

高度封装

只需要在配置文件中进行配置即可选择相应方法。设置静态化API和获取静态化API的方法

<?php


namespace App\Lib\Cache;

use App\Model\Video as VideoModel;
use EasySwoole\Config;
use EasySwoole\Core\Component\Cache\Cache;
use EasySwoole\Core\Component\Di;
use EasySwoole\Core\Http\Message\Status;

class Video
{
    /**
     * 设置静态API的方法
     * @throws \Exception
     */
    public function setIndexVideo()
    {
        $catIds = array_keys(Config::getInstance()->getConf("category"));
        array_unshift($catIds, 0);

        // 获取配置
        try {
            $cacheType = Config::getInstance()->getConf("base.indexCacheType");
        } catch (\Exception $e) {
            return $this->writeJson(Status::CODE_BAD_REQUEST,"请求失败");
        }

        $modelObj = new VideoModel();

        foreach ($catIds as $catId) {
            $condition = [];
            if (!empty($catId)) {
                $condition['cat_id'] = $catId;
            }

            try {
                $data = $modelObj->getVideoCacheData($condition);
            } catch (\Exception $e) {
                // 短信报警
                $data = [];
            }
            if (empty($data)) {

            }

            foreach ($data as &$list) {
                $list['create_time'] = date("Ymd H:i:s", $list["create_time"]);
                $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]);
            }

            switch ($cacheType) {
                case 'file':
                    $res = file_put_contents($this->getVideoCatIdFile($catId), json_encode($data));
                    break;
                case 'table':
                    $res = Cache::getInstance()->set($this->getCatKey($catId), $data);
                    break;
                case 'redis':
                    $res = Di::getInstance()->get("REDIS")->set($this->getCatKey($catId), $data);
                    break;
                default:
                    throw new \Exception("请求不合法");
                    break;
            }

            if(empty($res)){
            //    记录日志
            //    报警

            }

        }

    }

    /**
     * 获取数据的方法
     * @param $catId
     * @return array|bool|mixed|null|string
     * @throws \Exception
     */
    public function getCache($catId){
        $cacheType = Config::getInstance()->getConf("base.indexCacheType");
        switch ($cacheType){
            case 'file':
                $videoFile = $this->getVideoCatIdFile($catId);
                $videoData = is_file($videoFile) ? file_get_contents($videoFile) : [];
                $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
                break;
            case 'table':
                $videoData = Cache::getInstance()->get($this->getCatKey($catId));
                $videoData = !empty($videoData) ? $videoData : [];
                break;
            case 'redis':
                $videoData = Di::getInstance()->get("REDIS")->get($this->getCatKey($catId));
                $videoData = !empty($videoData) ? json_decode($videoData,true) : [];
                break;
            default:
                throw new \Exception("请求不合法");
                break;
        }
        return $videoData;
    }

    public function getVideoCatIdFile($catId = 0){
        return EASYSWOOLE_ROOT . "/webroot/video/json/" . $catId . ".json";
    }


    public function getCatKey($catId = 0){
        return "index_video_data_cat_id".$catId;
    }
}

只需修改配置文件

<?php

return [
    "indexCacheType" => "redis" // redis file table
];

控制器获取数据给前端

public function lists(){
    $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0;
    $videoData = (new VideoCache())->getCache($catId);
    $count = count($videoData);
    return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData));
}
]]>
https://www.askme-121.pw/static-api/feed/ 0
PHP+Swoole实现微信小程序客服即时通信聊天功能 https://www.askme-121.pw/php-swoole/ https://www.askme-121.pw/php-swoole/#respond Sat, 20 Jan 2024 06:51:11 +0000 https://www.askme-121.pw/?p=495 一、PHP7安装Swoole扩展

PHP swoole 扩展下载地址

Github:https://github.com/swoole/swoole-src/tags

php官方扩展库:http://pecl.php.net/package/swoole

开源中国:http://git.oschina.net/swoole/swoole/tags

1、自定义安装

# 下载

wget https://pecl.php.net/get/swoole-4.3.3.tgz

# 解压

tar zxf swoole-4.3.3.tgz

# 编译安装扩展

# 进入目录

cd swoole-4.3.3 

# 执行phpize命令,产生出configure可执行文件
# 如果不知道phpize路径在哪里 可以使用which phpize查看相应路径

/usr/bin/phpize   

# 进行配置  如果不知道php-config路径在哪里 可以使用which php-config   查看相应路径

./configure --with-php-config=/usr/bin/php-config   

# 编译和安装

make && make install 

vi /etc/php.ini

复制如下代码

extension=swoole.so

放到你所打开或新建的文件中即可,无需重启任何服务

# 查看扩展是否安装成功

php -m|grep swoole

2、宝塔面板安装PHP swoole扩展

如果感觉上述安装较为复杂,可以使用宝塔面板实现一键安装

二、配置nginx反向代理

1、使用xshell连接远程阿里云服务器

2、使用命令(find / -name nginx.conf)查找nginx.conf所在的配置文件

find / -name nginx.conf

3、使用命令(vim /etc/nginx/nginx.conf)查找进入到vim编辑器

vim /etc/nginx/nginx.conf

查看到可以引入/etc/nginx/conf.d/下的配置文件信息

4、使用命令(cd /etc/nginx/conf.d/)进入到该路径下,并新建配置文件:study.lishuo.net.conf

5、配置nginx反向代理,实现访问study.lishuo.net域名转发端口号到127.0.0.1:9511也就是转发到webscoket运行的端口号

# 反向代理的规则 study 这个名字自己随便起
upstream study{
  server 127.0.0.1:9511;
}
server {
        listen       80;
        server_name  study.lishuo.net;
        error_page 404 /404.html;
        location = /404.html {
        }
        location / {
          index index.php index.html index.htm;
          if (!-e $request_filename) {
                rewrite  ^(.*)$  /index.php?s=/$1  last;
          }
        #wss配置
        client_max_body_size 100m;
        proxy_redirect off;
        proxy_set_header Host $host;# http请求的主机域名
        proxy_set_header X-Real-IP $remote_addr;# 远程真实IP地址
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#反向代理之后转发之前的IP地址
        proxy_read_timeout 604800s;#websocket心跳时间,默认是60s
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://study;
       }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
         #添加下列信息,配置Nginx通过fastcgi方式处理您的PHP请求。
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9001;   #Nginx通过本机的9000端口将PHP请求转发给PHP-FPM进行处理。
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include fastcgi_params;   #Nginx调用fastcgi接口处理PHP请求。
        }
    }

三、微信小程序socket合法域名配置

1、登录到微信开放平台https://mp.weixin.qq.com/

2、开发=>开发管理=>开发设置,完成合法域名设置

3、到此配置已经完成了,接下来就是功能实现了,微信小程序+PHP代码

四、效果演示和代码

1、小程序端代码

小程序页面代码所在路径 /pages/contact/contact.wxml

<!--pages/contact/contact.wxml-->

<view>

<scroll-view scroll-y scroll-into-view='{{toView}}' style='height: {{scrollHeight}};'>
  <!-- <view class='scrollMsg'> -->
  <block wx:key wx:for='{{msgList}}' wx:for-index="index">

    <!-- 单个消息1 客服发出(左) -->
    <view wx:if='{{item.speaker=="server"}}' id='msg-{{index}}' style='display: flex; padding: 2vw 11vw 2vw 2vw;'>
      <view style='width: 11vw; height: 11vw;'>
        <image style='width: 11vw; height: 11vw; border-radius: 10rpx;' src='https://cdn.pixabay.com/photo/2020/02/10/12/47/girl-4836394__340.jpg'></image>
      </view>
      <view style='width: 4vw; height: 11vw; margin-left: 0.5vw; display: flex; align-items: center; z-index: 9;'>
        <view class="triangle_border_left"></view>
      </view>
      <view class='leftMsg'>{{item.content}}</view>
    </view>

    <!-- 单个消息2 用户发出(右) -->
    <view wx:else id='msg-{{index}}' style='display: flex; justify-content: flex-end; padding: 2vw 2vw 2vw 11vw;'>
      <view class='rightMsg'>{{item.content}}</view>
      <view style='width: 4vw; height: 11vw; margin-right: 0.5vw; display: flex; align-items: center; z-index: 9;'>
        <view class="triangle_border_right"></view>
      </view>
      <view style='width: 11vw; height: 11vw;'>
        <image style='width: 11vw; height: 11vw; border-radius: 10rpx;' src='https://cdn.pixabay.com/photo/2021/09/24/10/00/chick-6652163__340.jpg'></image>
      </view>
    </view>

  </block>
  <!-- </view> -->

  <!-- 占位 -->
  <view style='width: 100%; height: 18vw;'></view>
</scroll-view>

<view class='inputRoom' style='bottom: {{inputBottom}}'>
  <image style='width: 7vw; margin-left: 3.2vw;' src='https://img95.699pic.com/element/40030/6429.png_300.png' mode='widthFix'></image>
  <input bindconfirm='sendClick' adjust-position='{{false}}' value='{{inputVal}}' confirm-type='send' bindfocus='focus' bindblur='blur'></input>
</view>
</view>

小程序页面样式代码所在路径 /pages/contact/contact.wxss


/* pages/contact/contact.wxss */

page {
  background-color: #f1f1f1;
}

.inputRoom {
  width: 100vw;
  height: 16vw;
  border-top: 1px solid #cdcdcd;
  background-color: #f1f1f1;
  position: fixed;
  bottom: 0;
  display: flex;
  align-items: center;
  z-index: 20;
}

input {
  width: 76vw;
  height: 9.33vw;
  background-color: #fff;
  border-radius: 40rpx;
  margin-left: 2vw;
  padding: 0 3vw;
  font-size: 28rpx;
  color: #444;
}

.leftMsg {
  font-size: 35rpx;
  color: #444;
  line-height: 7vw;
  padding: 2vw 2.5vw;
  background-color: #fff;
  margin-left: -1.6vw;
  border-radius: 10rpx;
  z-index: 10;
}

.rightMsg {
  font-size: 35rpx;
  color: #444;
  line-height: 7vw;
  padding: 2vw 2.5vw;
  background-color: #96EB6A;
  margin-right: -1.6vw;
  border-radius: 10rpx;
  z-index: 10;
}

 /*向左*/
 .triangle_border_left {
  width: 0;
  height: 0;
  border-width: 10px 30px 30px 0;
  border-style: solid;
  border-color: transparent #fff transparent transparent;
          /*透明       黄   透明        透明 */
  margin: 40px auto;
  position: relative;
}


        /*向右*/
        .triangle_border_right {
          width: 0;
          height: 0;
          border-width: 0px 30px 20px 13px;
          border-style: solid;
          border-color: transparent transparent transparent #96EB6A;
                  /*透明       透明        透明         黄*/
          margin: 40px auto;
          position: relative;
      }

小程序配置文件代码所在路径 /pages/contact/contact.json


{
  "navigationBarTitleText":"柯作客服",
  "usingComponents": {
  
  }
}

小程序业务逻辑代码所在路径 /pages/contact/contact.js


// pages/contact/contact.js
const app = getApp();
var inputVal = '';
var msgList = [];
var windowWidth = wx.getSystemInfoSync().windowWidth;
var windowHeight = wx.getSystemInfoSync().windowHeight;
var keyHeight = 0;

/**
 * 初始化数据
 */
function initData(that) {
  //输入框的内容
  inputVal = '';
  //消息列表,包含客服和用户的聊天内容
  msgList = [{
      speaker: 'server',
      contentType: 'text',
      content: 'Hi,亲爱的小主,终于等到您啦!欢迎来到柯作店铺,很荣幸为您服务。'
    },
    {
      speaker: 'customer',
      contentType: 'text',
      content: '你高兴的太早了'
    }
  ]
  that.setData({
    msgList,
    inputVal
  })
}

Page({
  /**
   * 页面的初始数据
   */
  data: {
    scrollHeight: '100vh',
    inputBottom: 0
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {
    //初始化websocket连接
    this.chat();
    //监听心跳的方法
    this.webSocketXin();
    //聊天方法
    initData(this);

    //监听消息
    wx.onSocketMessage(res=>{
         //追加到消息列表里
        msgList.push(JSON.parse(res.data))
        inputVal = '';
        this.setData({
          msgList,
          inputVal
        });
    })


  },
  //页面卸载时间
  onUnload(){
    wx.closeSocket();
  },
  /**
   * 获取聚焦
   */
  focus: function(e) {
    keyHeight = e.detail.height;
    this.setData({
      scrollHeight: (windowHeight - keyHeight) + 'px'
    });
    this.setData({
      toView: 'msg-' + (msgList.length - 1),
      inputBottom: keyHeight + 'px'
    })
    //计算msg高度
    // calScrollHeight(this, keyHeight);

  },

  //失去聚焦(软键盘消失)
  blur: function(e) {
    this.setData({
      scrollHeight: '100vh',
      inputBottom: 0
    })
    this.setData({
      toView: 'msg-' + (msgList.length - 1)
    })
  },

  /**
   * 发送点击监听
   */
  sendClick: function(e) {
    //客户发的信息
    let customerMsg = {
      uid: 10,
      speaker: 'customer',
      contentType: 'text',
      content: e.detail.value
    };

     //关闭心跳包
     this.webSocketXin(60000, false)
    //发送给websocket
    wx.sendSocketMessage({
      data: JSON.stringify(customerMsg),
      success:res=>{
        //重启心跳包
        this.webSocketXin(40000, true)
      }  
    })

    //追加到消息列表里
    msgList.push(customerMsg)
    inputVal = '';
    this.setData({
      msgList,
      inputVal
    });
  },
  /**
   * 退回上一页
   */
  toBackClick: function() {
    wx.navigateBack({})
  },
  /**
   * websocket
   */
  chat(){
     //进行连接php的socket
     wx.connectSocket({
       //wss 协议相当于你要有一个ssl证书,https
       //ws  就相当于不实用证书  http
      url: 'ws://study.lishuo.net',
      success: function () {
        console.log('websocket连接成功~')
      },
      fail: function () {
        console.log('websocket连接失败~')
      }
    })
  },


  /**
   * 监听websocket心跳连接的方法
   */
  webSocketXin(time=60000,status=true){
    var timing;
    if(status == true){
      timing = setInterval(function () {
        console.log("当前心跳已重新连接");
        //循环执行代码
        wx.sendSocketMessage({
          data: JSON.stringify({
            type: 'active'
          }),
          fail(res) {
            //关闭连接
            wx.closeSocket();
            //提示
            wx.showToast({
              title: '当前聊天已断开',
              icon:'none'
            })
            clearInterval(timing);
            console.log("当前心跳已关闭");
          }
        });
      }, time) //循环时间,注意不要超过1分钟  
    } else {
      //关闭定时器
      clearInterval(timing);
      console.log("当前心跳已关闭");
    }
  

  }



})

2、服务端代码(PHP代码)

wechat_websocket.php


<?php

//创建WebSocket Server对象,监听0.0.0.0:9502端口
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9511);

//监听WebSocket连接打开事件
$ws->on('Open', function ($ws, $request) {
    echo $request->fd . '我连接上了';
});

//监听WebSocket消息事件
$ws->on('Message', function ($ws, $frame) {
    //把前台传过来的json字符串转成数组
    $params = json_decode($frame->data, true);
    //判断是否是心跳消息,如果是心跳消息
    if (isset($params['type']) && isset($params['type'])=='active'){
        echo '这是心跳监听消息';
    }else{
        //先判断当前用户有没有正在连接
        if (isset($params['uid']) && !empty($params['uid'] == 666)) {
            //去用户表查询当前用户  fd
            $fd = 2;
        } else {
            $fd = 1;
        }
        //客服id
        $ws->push($fd, json_encode($params, JSON_UNESCAPED_UNICODE));
    }
});

//监听WebSocket连接关闭事件
$ws->on('Close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();

五、部署

1、把服务端代码上传到Linux操作系统里

2、然后切到该目录下进行运行php wechat_websocket.php

php wechat_websocket.php
]]>
https://www.askme-121.pw/php-swoole/feed/ 0