THINKPHP5整合workerman+gateway


這次要開發聊天系統 , 需要用到WebSocket   我使用的是workerman+gateway,為了方便后面再用,做個簡單記錄

首先要特別注意的是,端口要開放,如果端口未開放,會出現連接時握手失敗的情況,這里我用的商品是  801   和  802

1、安裝workerman和gateway

composer require topthink/think-worker
composer require workerman/gatewayclient

2、添加server.php文件,后成需要在CLI模式下運行

#!/usr/bin/env php
<?php
ini_set('display_errors', 'on');
if(strpos(strtolower(PHP_OS), 'win') === 0)
{
    exit("start.php not support windows.\n");
}
// 檢查擴展
if(!extension_loaded('pcntl'))
{
    exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
if(!extension_loaded('posix'))
{
    exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
define('APP_PATH', __DIR__ . '/application/');
define('BIND_MODULE','push/Run');
// 加載框架引導文件
require __DIR__ . '/thinkphp/start.php';

3、創建push模塊新建兩個控制器Run.php    Events.php

Start.php

<?php
namespace app\push\controller;
use Workerman\Worker;
use GatewayWorker\Register;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
class Run extends Worker{
  /**
     * 構造函數
     * @access public
     */

    public function __construct(){
		//由於是手動添加,因此需要注冊命名空間,方便自動加載,具體代碼路徑以實際情況為准
		        \think\Loader::addNamespace([
		            'Workerman' => VENDOR_PATH . 'Workerman/workerman',
		            'GatewayWorker' =>VENDOR_PATH . 'Workerman/gateway-worker/src',
		        ]);
		        //初始化各個GatewayWorker
		//初始化register
		new Register('text://0.0.0.0:801');
		 
		 //初始化 bussinessWorker 進程
		$worker = new BusinessWorker();
		$worker->name = 'WebIMBusinessWorker';
		$worker->count = 4;
		$worker->registerAddress = '127.0.0.1:801';
		//設置處理業務的類,此處制定Events的命名空間
		$worker->eventHandler = '\app\push\controller\Events';
		// 初始化 gateway 進程
		$gateway = new Gateway("websocket://0.0.0.0:802");
		$gateway->name = 'WebIMGateway';
		$gateway->count = 4;
		$gateway->lanIp = '127.0.0.1';
		$gateway->startPort = 2900;
		$gateway->registerAddress = '127.0.0.1:801';
		 
		 //運行所有Worker;
		Worker::runAll();
    }
}

Events.php

<?php
namespace app\push\controller;
use GatewayWorker\Lib\Gateway;
/**
 * 主邏輯
 * 主要是處理 onConnect onMessage onClose 三個方法
 * onConnect 和 onClose 如果不需要可以不用實現並刪除
 */
class Events{
/**
    * 當客戶端發來消息時觸發
    * @param int $client_id 連接id
    * @param mixed $data 具體消息
    */
    public static function onMessage($client_id, $data){
       $message = json_decode($data, true);
       $message_type = $message['type'];
       switch($message_type) {
           case 'init':
               // uid
               $uid = $message['id'];
               // 設置session
               $_SESSION = [
                   'username' => $message['username'],
                   'avatar'   => isset($message['avatar'])?$message['avatar']:'',
                   'id'       => $uid,
                   'sign'     => $message['sign'],
					'type'=> $type, 
               ];
               // 將當前鏈接與uid綁定
               Gateway::bindUid($client_id, $uid);
               // 通知當前客戶端初始化
               $init_message = array(
                   'message_type' => 'init',
                   'id'           => $uid,
               );
               Gateway::sendToClient($client_id, json_encode($init_message));
               return;
               break;
           case 'chatMessage':
               // 聊天消息
               $type = $message['data']['to']['type'];
               $to_id = $message['data']['to']['id'];
               $uid = $_SESSION['id'];
 
               $chat_message = [
                    'message_type' => 'chatMessage',
                    'data' => [
                        'username' => $_SESSION['username'],
                        'avatar'   => $_SESSION['avatar'],
                        'id'       => $type === 'friend' ? $uid : $to_id,
                        'type'     => $type,
                        'content'  => htmlspecialchars($message['data']['mine']['content']),
                        'timestamp'=> time()*1000,
                    ]
               ];
         
               return Gateway::sendToUid($to_id, json_encode($chat_message));
               break;
           case 'hide':
           case 'online':
               $status_message = [
                   'message_type' => $message_type,
                   'id'           => $_SESSION['id'],
               ];
               $_SESSION['online'] = $message_type;
               Gateway::sendToAll(json_encode($status_message));
               return;
               break;
           case 'ping':
               return;
           default:
               echo "unknown message $data" . PHP_EOL;
       }
    }
    /**
     * 當客戶端連接時觸發
     * 如果業務不需此回調可以刪除onConnect
     * 
     * @param int $client_id 連接id
     */
    public static function onConnect($client_id)
    {
    	
        var_dump($client_id);
        Gateway::sendToClient($client_id,json_encode(['status'=>"success",'msg'=>"連接成功"]));
    	Gateway::sendToAll("連接成功");
    }
    /**
     * 當連接斷開時觸發的回調函數
     * @param $connection
     */
    public static function onClose($client_id){
       $logout_message = [
           'message_type' => 'logout',
           'id'           => $_SESSION['id']
       ];
       Gateway::sendToAll(json_encode($logout_message));
    }
    /**
     * 當客戶端的連接上發生錯誤時觸發
     * @param $connection
     * @param $code
     * @param $msg
     */
    public static function onError($client_id, $code, $msg)
    {
        echo "error $code $msg\n";
    }
    /**
     * 每個進程啟動
     * @param $worker
     */
    public static function onWorkerStart($worker)
    {
    }
}

然后執行命令開啟進程

php server.php start

 


前端示例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
 
<script>
    ws = new WebSocket("ws:0.0.0.0:802");    //填你線上服務端地址
    // 服務端主動推送消息時會觸發這里的onmessage
    ws.onopen = function(){
        console.info("與服務端連接成功");
        ws.send('test msg\n');//相當於發送一個初始化信息
        console.info("向服務端發送心跳包字符串");
    };
 
    ws.onmessage = function(e){
        // json數據轉換成js對象
        var data = eval("("+e.data+")");
        console.log(data.msg);
    };
 
    ws.onclose = function(e){
        console.log(e);
    };
 
</script>
 
</body>
</html>

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM