swoole實現多對多羣聊(二)

之前發佈了第一個版本的羣聊實現,大家可以去看一下上篇文章 swoole實現多對多羣聊

這篇文章主要是在實現羣聊的基礎上加了日誌功能以及代碼的優化
老樣子,直接上代碼

PHP代碼

<?php

define('HOST', '0.0.0.0');
define('PORT', '19501');
define('WORKER_NUM', 2);
define('TASK_WORKER_NUM', 4);

class WebsocktDemo
{
    public $ws = null;
    public $redis = null;
    
    public $pdo = null;
    
    public function __construct()
    {

        $this->ws = new Swoole\WebSocket\Server(HOST, PORT);
        $this->redis = $this->redis();
        $this->redis->auth("***********");
        
		//實例化數據庫鏈接
        $this->pdo = new PDO(
            "mysql:host=********;dbname=******;charset=utf8;",
            "********",
            "********",[\PDO::ATTR_CASE => \PDO::CASE_NATURAL]
        );
        
		//創建token找幾個小弟
        $this->ws->set([
            'worker_num' => WORKER_NUM,
            'task_worker_num' => TASK_WORKER_NUM,
        ]);

        //$this->ws->on("start", [$this, 'onStart']);
        $this->ws->on("open", [$this, 'onOpen']);
        $this->ws->on('message',[$this,'onMessage']);
        $this->ws->on('close',[$this,'onClose']);
        
		//token線程監聽
        $this->ws->on("task", [$this, 'onTask']);
        $this->ws->on('finish',[$this,'onFinish']);
        
        $this->ws->start();
    }
	/**
     * 獲取redis實例
     * @return null|Redis
     */
    protected function redis()
    {
        static $redis = null;
        if (is_null($redis)) {
            $redis = new \Redis();
            $redis->connect("*********");
        }

        return $redis;
    }
    
    /**
     * 監聽用戶鏈接事件,鏈接時需要帶用戶id與房間id參數,再把用戶存到房間域中
     */
    public function onOpen($server, $request){
        //加入房間域
        $this->redis->hset($request->get['room'], $request->get['uid'], $request->fd);
        //加入組集合
        $this->redis->sadd('group', $request->get['room']);
		
		//鏈接後,插入一條日誌
		self::recordLog($request->fd, $request->get['uid'], $request->get['room'], "add");
    }
    
    /**
     *   $room    當前房間id
     *   $arr    組裝數據
     */
    public function push_room($room, $arr)
    {
        //獲取在線用戶的fd
        $push_arr = $this->redis->hvals($room);
        //推送
        foreach ($push_arr as $v) {
            $this->ws->push($v, json_encode($arr));
        }
    }
    
    /**
     * 監聽接收事件的回調
     */
    public function onMessage($server, $frame)
    {
        //在接收數據的時候進行推送,每次發送消息都要帶上標記信息(uid,room)
        $data = json_decode($frame->data, true);
        //組裝數據判斷類型
        switch ($data['type']){
            case "change"://發送消息
                $arr['name'] = $data['name'];
                $arr['content'] = $data['content'];
                $arr['type'] = "change";
                $arr['uid'] = $data['uid'];
                $arr['room'] = $data['room'];
                break;
        }
        //找小弟去推送消息到房間
        $this->ws->task($data);
    }
    
	/**
     * 監聽關閉事件的回調,這一步照搬,沒有做修改
     */
    public function onClose($ser, $fd)
    {
        //退出並刪除多餘的分組,並更新日誌狀態
        $this->ws->task(array("type"=>"clean","fd"=>$fd));
    }
	/**
	* 線程小弟
	*/
    public function onTask($server, $task_id, $from_id, $data)
    {
        switch ($data['type']){
            case "change":
                self::push_room($data['room'], $data);
                break;
            case "clean":
                self::cleanUser($data);
                break;
        }
        return true;
    }

	/**
	* 線程任務完成回調事件,這裏不需要回調什麼,但是必須要綁定這個事件
	*/
    public function onFinish($server, $task_id, $data)
    {
        echo "Task#$task_id finished";
    }
	
	//退出房間事件
	public function cleanUser($data){
        //退出並刪除多餘的分組fd
        $group = $this->redis->sMembers('group');
        foreach ($group as $v) {
            $fangjian = $this->redis->hgetall($v);
            if(empty($fangjian)) continue;
            foreach ($fangjian as $k => $vv) {
                if ($data['fd'] == $vv) {
                    $this->redis->hdel($v, $k);
                    self::recordLog($vv, $k, $v, "close");
                    //跳出兩層循環
                    break 2;
                }
            }
        }
    }
    
	//房間記錄 fd-uid-room
    private function recordLog($fd, $uid, $room_id, $type)
    {
        $sql = "";
        switch ($type){
            case "add":
                $sql = "***********";
                break;
            case "close":
                $sql = "**********";
                break;
        }
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute();
        } catch (Exception $e) {
            echo "recordLog error";
        }
    }
  }
  
  //最後實例化對象
  new WebsocktDemo();
    

前端方面不需要改動,主要是優化了一下下代碼,加了一個統計在線時長的功能,找了個跑腿的小弟,這裏要注意一下,小弟不是越多越好,小弟也是要吃飯的,就算是閒着也會暫用你的內存,所以這裏只創建了4個,如果有什麼寫得不好的地方大家可以指導一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章