当前位置 知且行 php swoole框架-swoft实现程圣母与云天明对话功能 下一篇:

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

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

转载必须注明出处:https://www.zhiqiexing.com/56.html

关于我

我希望能成为一个认真、有趣、创造更多价值的人
关注微信
微信扫一扫关注我

微信扫一扫关注我

返回顶部