主页

TP5或fastadmin连接Workerman/websocket并使用宝塔开启wss反代理

薄荷べ浅浅绿   IP属地: 青海
0 点赞 0阅读 2601

需求:用户小程序下单成功管理员后台通过websocket通知业务员有新订单的提示,但是小程序都是要开启https才能使用的,需要在宝塔配置一下,配置如下


location ~* (runtime|application)/{
	return 403;
}
location / {
	if (!-e $request_filename){
		rewrite  ^(.*)$  /index.php?s=$1  last;   break;
	}
}
location /wss
    {
            proxy_pass http://127.0.0.1:2346;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            rewrite /wss/(.*) /$1 break;
            proxy_redirect off;
    }
我的配置文件如下,仅供参考
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}
# 这里的backend可以改成自己需要的名称,我自己用的是wss。
upstream wss {
  # 这里的localhost是映射本地服务器,也可以是外网ip,2345是我ws开启的端口。
  server localhost:2346;
}

server
{
    listen 80 default_server;
		listen 443 ssl http2 default_server;
    server_name xxxxx.cn www.laiyashiji.cn;
    index index.php index.html index.htm default.php default.htm default.html;
    root /www/wwwroot/xxxxx.cn/hotel/public;

    #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
    #error_page 404/404.html;
    ssl_certificate    /www/server/panel/vhost/cert/xxxxx.cn/fullchain.pem;
    ssl_certificate_key    /www/server/panel/vhost/cert/xxxxx.cn/privkey.pem;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES828:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    error_page 497  https://$host$request_uri;
		#SSL-END

    #ERROR-PAGE-START  错误页配置,可以注释、删除或修改
    #error_page 404 /404.html;
    #error_page 502 /502.html;
    #ERROR-PAGE-END

    #PHP-INFO-START  PHP引用配置,可以注释或修改
    

	include enable-php-74.conf;
    #PHP-INFO-END

    #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
    include /www/server/panel/vhost/rewrite/xxxxx.cn.conf;
    #REWRITE-END

    #禁止访问的文件或目录
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
    {
        return 404;
    }

    #一键申请SSL证书验证目录相关设置
    location ~ \.well-known{
        allow all;
    }

    #禁止在证书验证目录放入敏感文件
    if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
        expires      30d;
        error_log /dev/null;
        access_log /dev/null;
    }
    location ~ .*\.(js|css)?$
    {
        expires      12h;
        error_log /dev/null;
        access_log /dev/null;
    }
	access_log  /www/wwwlogs/xxxxx.cn.log;
    error_log  /www/wwwlogs/xxxxx.cn.error.log;
}

参考链接地址:https://cloud.tencent.com/developer/article/2013269
参考链接地址:https://www.cnblogs.com/chengwens/p/17721401.html


代理开启成功后需要配置TP框架Workerman实现websocket前后端通信了,

第一步创建一个worker文件目录和Worker.php文件

代码


<?php
namespace app\worker\controller;
use think\worker\Server;
use Workerman\Lib\Timer;
class Worker extends Server
{
    protected $socket = 'websocket://127.0.0.1:2346';
    protected $processes = 1;//进程数
    protected $worker; 
    protected $HEARTBEAT_TIME = '60';//心跳时间,后端主动关闭客户端
    public function __construct()
    {
        parent::__construct();
        $this->worker->uidConnections = array(); //用户链接池 存储映射
    }

    /**
     * 接收消息
     * @param $connection
     * @param $data
     */
    public function onMessage($connection, $data)
    {
        //客户端与后端消息链接唯一标识
        $uid = $connection->uid;
        if($uid) {
            $data=json_decode($data,true);
            $message=['code'=>0,'msg'=>'你还活着','type'=>0,'data'=>[]];
            if(!$data){
                return true;
            }
            //区分各种业务处理
            $method = $data['method'];
            $is_push=0;//是否主动推送消息
            switch ($method) {
                //创建聊天室
                case 'add_chat':
                    $is_push=1;//开启推送
                    $chat_id=1111;//你的聊天室id 返回给前端
                    $message=['code'=>0,'msg'=>'创建成功','type'=>-1,'data'=>$chat_id];
                    break;
                //发送消息
                case 'send':
                    $is_push=1;//开启推送
                    $message='你处理的消息信息';
                    
                    //如果对方在线 向对方发送一条消息
                    $this->sendMessageByUid($data['user_id'],$message);
                    
                    $message2='聊天消息列表';
                    //对方在线 更新对方消息接收列表(没做分页处理,分页后台发送单条信息前端使用push)
                    $this->sendMessageByUid($data['user_id'],$message2);
                    
                    break;
                 //客户端主动来取消息列表
                case 'list':    
                    $is_push=1;//开启推送
                    $message='聊天消息列表';
                    break;
                //客户端主动取聊天室内容
                case 'chat_list':
                    $is_push=1;//开启推送
                    $post=[
                        'user_id'=>$uid,
                        'chat_id'=>$data['chat_id'],//聊天室id
                        'page'=>$data['page'],//分页数
                    ];
                    $message=ChatList::getList($post);
                    break;
            }
           //向自己推送消息
            if($is_push){
                $this->sendMessageByUid($uid,$message);
            }
        }else{//查询不到用户信息,关闭此连接
            $this->onClose($connection);
        }
    }
    // 向所有验证的用户推送数据
    function broadcast($message)
    {
        foreach($this->worker->uidConnections as $connection)
        {
            $connection->send($message);
        }
    }

    // 针对uid推送数据
    function sendMessageByUid($uid, $message)
    {

        if(isset($this->worker->uidConnections[$uid]))
        {
            $connection = $this->worker->uidConnections[$uid];
            $connection->send(json_encode($message,true));
        }
    }

    /**
     * 当连接建立时触发的回调函数
     * @param $connection
     */
    public function onConnect($connection)
    {
        $connection->onWebSocketConnect = function($connection , $http_header)
        {
            // 可以在这里判断连接来源是否合法,不合法就关掉连接
            // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket连接
            // $arr_request=[
            //     'https://laiyashiji.cn',
            //     '域名2',
            // ];
            // //判断是否合法连接
            // if(in_array( http_header['Host'],$arr_request) !=true)
            // {
            //     $connection->close();
            // }
            $connection->send('received: ' . $http_header);
            // 存客户端与websocket的映射,唯一连接标识(!!!关键)
            if(!isset($connection->uid))
            {
                // $connection->uid = $_SERVER['HTTP_TOKEN'];
                // $this->worker->uidConnections[$connection->uid]=$connection;
                // return;
            }
        };
    }

    /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {
//        //代表用户下线,清除用户信息
        if(isset($connection->uid))
        {
            // 连接断开时删除映射
            unset($this->worker->uidConnections[$connection->uid]);
        }
    }

    /**
     * 当客户端的连接上发生错误时触发
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        echo "error $code $msg\n";
    }

    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
        //每10秒检查一下客户端是否还连着服务端。超时未相应也会主动关闭与客户端的连接
        Timer::add(10, function()use($worker){
            $time_now = time();
            foreach($worker->connections as $connection) {
                // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
                if (empty($connection->lastMessageTime)) {
                    $connection->lastMessageTime = $time_now;
                    continue;
                }
                $diff_time = $time_now - $connection->lastMessageTime;
                $msg = '距离上次通话已经过去'.$diff_time.'秒';
                $message=['code'=>0,'msg'=>$msg,'type'=>0,'data'=>[]];
                $connection->send(json_encode($message,true));
                // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
                if ($time_now - $connection->lastMessageTime > $this->HEARTBEAT_TIME) {
                          $connection->close();
                }
            }
        });
    }
}


第二步在项目根目录的public下创建workerman的启动文件server.php

<?php
define('APP_PATH', __DIR__ . '/../application/');
define('BIND_MODULE','worker/Worker');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';

第三步安装workerman,通过 composer require workerman/workerman 安装,最好,最好,最好安装workerman 4.0以上的版本,本人一开始用3.5.35版本用同样的代码和配置和反代理就是运行不起来,但是又不报错,想了半天想起以前在本地搭建过一个workerman的,然后看了一下版本,抱着试一下的心态把workerman的库文件复制到项目中取,结果就运行起来了

{
    "require": {
        "workerman/workerman": "^4.0"
    }
}


第四步需要启动workerman,还记得刚刚创建的server.php文件吗,在服务器终端运行 php server.php start 这里没有加-d,在调试阶段就不要加-d了,如果觉得代码没啥问题了就使用 php server.php start -d 运行

如果在运行中出现端口被占用可以用kill -9 端口号 停掉,如

kill -9 25529
查看端口占用情况,如果遇到启动成功但是查询不到端口建议换一下workerman版本试试


netstat -anp | grep 2346
查看所有服务,可以查看server.php服务有没有启用成功
ps  aux

第五步前端连接websocket

连接成功


© 著作权归作者所有,转载或内容合作请联系作者