美文网首页程序员程序猿的进阶屋
swoole框架-swoft实现程圣母与云天明对话功能

swoole框架-swoft实现程圣母与云天明对话功能

作者: 闲睡猫 | 来源:发表于2018-08-01 15:51 被阅读18次

    当万有引力号启动广播按钮,向宇宙发送三体星的坐标时,地球已经失去了任何侵略价值。三体人将所有在地球的资源全部撤走,但在临别时,安排了程圣母与云天明的远程会话。接下来,我们用swoft来实现他们两人的聊天功能。

    光年之外的对话

    PHP果真是宇宙最强的语言,星际通话也能办到...

    对swoft不了解的同学请看 swoole框架-swoft初体验

    启动ws服务

    ☁  swoft [master] ⚡ sudo php bin/swoft ws:start
    Password:
                                     Server Information
    ************************************************************************************
    * WS   | host: 0.0.0.0, port: 80, type: 1, worker: 1, mode: 3 (http is Enabled)
    * TCP  | host: 0.0.0.0, port: 8099, type: 1, worker: 1 (Enabled)
    ************************************************************************************
    Server has been started. (master PID: 3954, manager PID: 3955)
    You can use CTRL + C to stop run.
    

    创建http服务的聊天控制器

    swoft 提供了生成控制器文件的命令行

    ☁  swoft [master] ⚡ php bin/swoft gen:controller chat --prefix /chat -y
    
    Class data:
    {
        "name": "chat",
        "suffix": "Controller",
        "namespace": "App\\Controllers",
        "className": "ChatController",
        "prefix": "/chat",
        "idVar": "{id}"
    }
    

    swoft/app/Controllers/ChatController.php

    <?php
    namespace App\Controllers;
    
    use Swoft\Http\Server\Bean\Annotation\Controller;
    use Swoft\Http\Server\Bean\Annotation\RequestMapping;
    use Swoft\Http\Server\Bean\Annotation\RequestMethod;
    
    /**
     * Class ChatController
     * @Controller(prefix="/chat")
     * @package App\Controllers
     */
    class ChatController{
        /**
         * @RequestMapping(route="/chat/{uid}", method=RequestMethod::GET)
         * @param int $uid
         * @return \Swoft\Http\Message\Server\Response|\think\response\View
         */
        public function index(int $uid)
        {
            $users = [
                1 => '程心',
                2 => '云天明',
            ];
            $receiveUid = $uid == 1 ? 2 : 1;
            $userName = $users[$uid];
            $data = compact('uid', 'userName', 'receiveUid');
            return view('chat/index', $data);
        }
    }
    

    创建视图文件

    视图文件用vue.js搭建,对于vue.js不熟悉的同学,参见 实例学习vue.js目录

    swoft/resources/views/chat/index.php

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.0/css/bootstrap.min.css">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <style>
        .container {
            margin-top: 2%;
        }
    </style>
    <body>
    <div class="container">
        <div id="app">
            <h3 class="offset-md-3">{{ userName }}</h3>
            <div class="alert alert-warning col-sm-7">
                <strong>警告!</strong> 你们的聊天信息已经被全程监控...
            </div>
            <div v-for="(value, key) in list" :class="className(value.uid)" >
                {{ value.content }}
            </div>
            <div>
                <form action="" method="post" class="form-horizontal" role="form">
                    <div class="form-group">
                        <div class="col-sm-8">
                            <input type="text" class="form-control" placeholder="远在光年之外的你们,想聊些什么呢..." v-model="content" required="required">
                        </div>
                    </div>
                    <button type="submit" class="btn btn-info offset-md-6" @click.prevent="pushData">发送</button>
                </form>
            </div>
        </div>
    </div>
    <script>
        let ws = new WebSocket('ws://127.0.0.1/chat')
        ws.onopen = function() {
            let data = {
                'sendUid': vm.$data.uid,
                'type': 'bind',
                'receiveUid': '',
            }
            // 将uid推送到服务端,与fd进行绑定
            ws.send(JSON.stringify(data));
        }
        ws.onmessage = function(evt) {
            let data = JSON.parse(evt.data)
            if (data.content) {
                vm.$data.list.push(data)
            }
        }
        ws.onclose = function() {
            console.log('连接关闭')
        }
    
        let vm = new Vue({
            el: "#app",
            data: {
                list: [],
                content: '',
                uid: <?=$uid?>,
                receiveUid: <?=$receiveUid?>,
                userName: '<?=$userName?>',
            },
            methods: {
                className(uid) {
                    if (uid === this.uid) {
                        return 'alert alert-success col-md-2'
                    } else {
                        return 'alert alert-danger col-md-2 offset-md-5'
                    }
                },
                pushData() {
                    let data = {
                        'sendUid': this.uid,
                        'receiveUid': this.receiveUid,
                        'content': this.content,
                        'type': 'chat'
                    }
                    ws.send(JSON.stringify(data));
                    this.content = ''
                }
            },
    
        })
    </script>
    </body>
    </html>
    

    创建ws控制器

    ☁  swoft [master] ⚡ php bin/swoft gen:websocket chat --prefix /chat
    
    Class data:
    {
        "name": "chat",
        "suffix": "Controller",
        "namespace": "App\\WebSocket",
        "className": "ChatController",
        "prefix": "/chat"
    }
    
    <?php
    
    namespace App\WebSocket;
    
    use Swoft\Http\Message\Server\Request;
    use Swoft\Http\Message\Server\Response;
    use Swoft\WebSocket\Server\Bean\Annotation\WebSocket;
    use Swoft\WebSocket\Server\HandlerInterface;
    use Swoole\WebSocket\Frame;
    use Swoole\WebSocket\Server;
    
    /**
     * Class ChatController - This is an controller for handle websocket
     * @package App\WebSocket
     * @WebSocket("chat")
     */
    class ChatController implements HandlerInterface
    {
        /**
         * @param Request $request
         * @param Response $response
         * @return array
         * [
         *  self::HANDSHAKE_OK,
         *  $response
         * ]
         */
        public function checkHandshake(Request $request, Response $response): array
        {
            return [self::HANDSHAKE_OK, $response];
        }
    
        /**
         * @param Server $server
         * @param Request $request
         * @param int $fd
         * @return mixed
         */
        public function onOpen(Server $server, Request $request, int $fd)
        {
        }
    
        /**
         * @param Server $server
         * @param Frame $frame
         * @return mixed
         */
        public function onMessage(Server $server, Frame $frame)
        {
            $fd = $frame->fd;
            $data = json_decode($frame->data, true);
            if ($data['type'] == 'bind') {
                // 将uid与fd绑定
                $server->bind($fd, $data['sendUid']);
            }
            $start_fd = 0;
            while(true)
            {
                // 获取所有fd连接
                $conn_list = $server->getClientList($start_fd, 10);
                if ($conn_list === false or count($conn_list) === 0)
                {
                    break;
                }
                $start_fd = end($conn_list);
                foreach($conn_list as $v)
                {
                    // 根据fd获取uid
                    $connection = $server->connection_info($v);
                    if (isset($connection['uid']) && in_array($connection['uid'], [$data['receiveUid'], $data['sendUid']])) {
                        if (isset($data['content'])) {
                            $response = [
                                'type' => 'response',
                                'content' => $data['content'],
                                'uid' => $data['receiveUid'],
                            ];
                            if ($v != $fd) { // 避免重复发送给消息发起者的fd
                                $server->push($v, json_encode($response, true));
                            }
                        }
                    }
                }
            }
            // 推送消息给客户端
            \Swoft::$server->sendTo($fd, $frame->data);
        }
    
        /**
         * @param Server $server
         * @param int $fd
         * @return mixed
         */
        public function onClose(Server $server, int $fd)
        {
            // do something. eg. record log
        }
    }
    

    结果演示

    建议同时多开几个浏览器窗口,访问http://127.0.0.1/chat/1http://127.0.0.1/chat/2

    切换窗口进行消息发送,观察其他窗口的数据变动

    程心与云天明对话.gif

    如果觉得本文对你有所帮助,点个赞,或者赏杯咖啡钱,你的认可对我很重要

    相关文章

      网友评论

      本文标题:swoole框架-swoft实现程圣母与云天明对话功能

      本文链接:https://www.haomeiwen.com/subject/dgrrvftx.html