Swoole 实现聊天室模块

1. 聊天室模块发送数据交互处理;

  • 创建 js 文件:public/static/live/js/chart-push.js
$(function(){

    $('#discuss-box').keydown(function(event){
        // 回车事件
        if(event.keyCode == 13){
            var text = $(this).val();
            var url = 'http://192.168.2.214:8811?s=index/chart';
            var data = {'content':text, 'game_id':1};

            $.post(url, data, function (result) {
                //to do
                $(this).val("");
            }, 'json');

        }

    });

});

2. 聊天室功能。

  • 创建 js 文件:public/static/live/js/chart.js
// 地址或者端口号要和直播间区分开来
var wsUrl = "ws://192.168.2.214:8812";

var websocket = new WebSocket(wsUrl);

// 实例对象的 onopen 属性
websocket.onopen = function(evt){
    console.log("connected-swoole-success");
}

// 实例化 onmessage
websocket.onmessage = function(evt){
    console.log("ws-server-return-chart-data:" + evt.data);
    // to do
    pushChart(evt.data)

}

// 实例化 close
websocket.onclose = function(evt){
    console.log("close");
}

// onerror
websocket.onerror = function(evt, e){
    console.log("error" + evt.data);
}


function pushChart(data){
    data = JSON.parse(data);
    html = '<div class="comment">';
    html += '<span>'+data.user+'</span>';
    html += '<span>'+data.content+'</span>';
    html += '</div>';

    $('#comments').prepend(html);

}
  • 创建: application/index/controller/Chart.php
<?php
namespace app\index\controller;

use app\common\lib\Util;

class Chart{


    public function index(){

        // 登录

        if(empty($_POST['game_id'])){
            return Util::show(config('code.error'), 'error');
        }

        if(empty($_POST['content'])){
            return Util::show(config('code.error'), 'error');
        }

        $data = [
            'user' => '用户'. rand(0, 2000),
            'content' => $_POST['content']
        ];

        // 推荐使用connections这种方式,redis方式也可以
        // ports[1] 为 8812 端口
        // 这里也可以丢进 task
        foreach($_POST['http_server']->ports[1]->connections as $fd) {
            $_POST['http_server']->push($fd, json_encode($data));
        }

        return Util::show(config('code.success'), 'ok', $data);

    }

}
  • 修改 ws.php
<?php

class Ws {
    CONST HOST = "0.0.0.0";
    CONST PORT = 8811;
    CONST CHART_PORT = 8812;

    public $ws = null;
    public function __construct() {
        $this->ws = new swoole_websocket_server(self::HOST, self::PORT);

        // 监听端口:https://wiki.swoole.com/wiki/page/525.html
        $this->ws->listen(self::HOST, self::CHART_PORT, SWOOLE_SOCK_TCP);

        $this->ws->set([
            'worker_num' => 4,
            'task_worker_num' => 4,
            'enable_static_handler' => true,    // 开启静态支持
            'document_root'	=> "/data/project/test/swoole/tp5/public/static",
        ]);

        // ws
        $this->ws->on("open", [$this, 'onOpen']);
        $this->ws->on("message", [$this, 'onMessage']);

        $this->ws->on("workerStart", [$this, 'onWorkerStart']);
        $this->ws->on("request", [$this, 'onRequest']);

        $this->ws->on("task", [$this, 'onTask']);
        $this->ws->on("finish", [$this, 'onFinish']);
        $this->ws->on("close", [$this, 'onClose']);


        $this->ws->start();
    }


    /**
     * @param $server
     * @param $worker_id
     */
    public function onWorkerStart($server,  $worker_id) {
        // 定义应用目录
        define('APP_PATH', __DIR__ . '/../application/');
        // 加载框架文件
        // 下次所有请求过来就不需要一一加载
        // 作用其它的回调时,能找到框架里的内容
        // require __DIR__ . '/../thinkphp/base.php';
        require __DIR__ . '/../thinkphp/start.php';
    }

    /**
     * request 回调
     * @param $request
     * @param $response
     */
    public function onRequest($request, $response) {
        $_SERVER = [];
        if(isset($request->server)) {
            foreach($request->server as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        if(isset($request->header)) {
            foreach($request->header as $k => $v) {
                $_SERVER[strtoupper($k)] = $v;
            }
        }

        $_FILES = [];
        if(isset($request->files)) {
            foreach($request->files as $k => $v) {
                $_FILES[$k] = $v;
            }
        }

        $_GET = [];
        if(isset($request->get)) {
            foreach($request->get as $k => $v) {
                $_GET[$k] = $v;
            }
        }

        $_POST = [];
        if(isset($request->post)) {
            foreach($request->post as $k => $v) {
                $_POST[$k] = $v;
            }
        }

        $_POST['http_server'] = $this->ws;

        ob_start();
        try{
            think\Container::get('app', [APP_PATH])->run()->send();
        }catch(\Exception $e){
            // todo
        }

        $res = ob_get_contents();
        ob_end_clean();
        $response->end($res);
    }


    /**
     * @param $serv
     * @param $task_id
     * @param $workerId
     * @param $data
     */
    public function onTask($serv, $task_id, $workerId, $data){
        // 分发 task 任务机制,让不同的任务走不同逻辑
        $obj = new app\common\lib\task\Task;

        $method = $data['method'];
        // 执行投放过来的 $method 变量 方法
        // 这里需要判断一些值是否存在
        $flag = $obj->$method($data['data'], $serv);

        return $flag;

        // print_R($data);
        // return "on task finish";
    }


    /**
     * @param $serv
     * @param $taskId
     * @param $data
     */
    public function onFinish($serv, $taskId, $data){
        // 这里的 $data 是 onTask() 方法 return 的内容
        echo "taskId:{$taskId}\n";
        echo "finish-data-success:{$data}\n";
    }


    /**
     * 监听 ws 连接事件
     * @param $ws
     * @param $request
     */
    public function onOpen($ws, $request){
        $info = $ws->connection_info($request->fd);
        // 直播的 fd 放入 redis,聊天室的 fd 不放进去
        if($info['server_port'] == self::PORT){
            // 把 $request->fd 数据 放入集合(sets)
            \app\common\lib\redis\Predis::getInstance()->sAdd(config('redis.live_game_key'), $request->fd);
        }

       echo "open - {$request->fd} \n";

    }


    /**
     * 监听 ws 消息事件
     * @param $ws
     * @param $frame
     */
    public function onMessage($ws, $frame){
        // server 端收到来自客户端的消息后,就会向客户端发送消息
        // 后台解说员把直播赛事的数据推到这来的时候,就在 onMessage() 这里把所有连接到 WebSocket 的客户端
        // 也可以理解为用户安全看直播的页面,把数据都推给 client 端
        echo "server-push-message:{$frame->data}\n";
        // $ws->push($frame->fd, "server-push:" . date("Y-m-d H:i:s"));
    }


    /**
     * @param $ws
     * @param $fd
     */
    public function onClose($ws, $fd){
        // 把 $fd 数据 从有序集合里删除
        \app\common\lib\redis\Predis::getInstance()->sRem(config('redis.live_game_key'), $fd);
        echo "closed - clientId: {$fd} \n";
    }


}

new Ws();
发布了119 篇原创文章 · 获赞 12 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/hualaoshuan/article/details/101716157