轻松学会Workerman实战

用户进入聊天室

此课程是收费课程,请先购买或加入会员后再访问。

新增组件

聊天室这里,我们到resource/assets/js/components里,新增一个叫做ChatRoom.vue的组件,里面放这样的代码。这里我用bootstrap做了一个非常基础的聊天的界面。后面的课程就是把这些功能一点点实现。

<template>
    <div class="container">
        <a href="?room_id=1" class="btn btn-danger">吃货人生</a>
        <a href="?room_id=2" class="btn btn-primary">技术探讨</a>
        <hr class="divider">

        <div class="row">
        <div class="col-md-8">
                <div class="panel panel-default">
                    <div class="panel-heading">聊天室</div>
                    <div class="panel-body">

                        <div class="messages">
                            <div class="media">
                                <div class="media-left">
                                    <a href="#">
                                        <img class="media-object img-circle" src="https://images.itfun.tv/user/avatar/174/fd44fc.png">
                                    </a>
                                </div>
                                <div class="media-body">
                                    <p class="time">2017-01-18 20:35:21</p>
                                    <h4 class="media-heading">Aaron</h4>
                                    哎哟,这不是小皮皮吗?你吃了没啊?
                                </div>
                            </div>

                            <div class="media">
                                <div class="media-left">
                                    <a href="#">
                                        <img class="media-object img-circle" src="https://images.itfun.tv/user/avatar/118/0865a4.png">
                                    </a>
                                </div>
                                <div class="media-body">
                                    <p class="time">2017-01-18 20:35:21</p>
                                    <h4 class="media-heading">Pipi</h4>
                                    我都快撑死了,我今天吃了蒸羊羔、蒸熊掌、蒸鹿尾儿、烧花鸭、烧雏鸡、烧子鹅、卤猪、卤鸭、酱鸡、腊肉、松花小肚儿、晾肉、香肠儿、什锦苏盘、熏鸡白肚儿、清蒸八宝猪...
                                </div>
                            </div>

                            <div class="media">
                                <div class="media-left">
                                    <a href="#">
                                        <img class="media-object img-circle" src="https://images.itfun.tv/user/avatar/174/fd44fc.png">
                                    </a>
                                </div>
                                <div class="media-body">
                                    <p class="time">2017-01-18 20:35:21</p>
                                    <h4 class="media-heading">Aaron</h4>
                                    皮皮啊,你可长点心把。再这么吃下去,都要变成小猪崽子了。
                                </div>
                            </div>

                            <div class="media">
                                <div class="media-left">
                                    <a href="#">
                                        <img class="media-object img-circle" src="https://images.itfun.tv/user/avatar/118/0865a4.png">
                                    </a>
                                </div>
                                <div class="media-body">
                                    <p class="time">2017-01-18 20:35:21</p>
                                    <h4 class="media-heading">Pipi</h4>
                                    你大爷!!!
                                </div>
                            </div>
                        </div>

                    </div>
                </div>
            </div>

            <div class="col-md-4">
                <div class="panel panel-default">
                    <div class="panel-heading">在线用户</div>

                    <div class="panel-body">
                        <ul class="list-group">
                            <li class="list-group-item">
                                <img src="https://images.itfun.tv/user/avatar/174/fd44fc.png" class="img-circle">
                                Aaron
                            </li>
                            <li class="list-group-item">
                                <img src="https://images.itfun.tv/user/avatar/118/0865a4.png" class="img-circle">
                                Pipi
                            </li>
                        </ul>
                    </div>

                </div>
            </div>
        </div>

        <form>
            <div class="form-group">
                <label for="user_id">私聊</label>

                <select class="form-control" id="user_id">
                    <option>所有人</option>
                    <option>Aaron</option>
                    <option>Pipi</option>
                </select>
            </div>

            <div class="form-group">
                <label for="content">内容</label>
                <textarea class="form-control" rows="3" id="content"></textarea>
            </div>

            <button type="submit" class="btn btn-default">提交</button>
        </form>
    </div>
</template>

<script>
    export default {
        data() {
            return {

            }
        },
        created() {

        },
        methods: {

        }
    }
</script>


<style scoped>
    .panel-body {
        height: 480px;
        overflow: auto;
    }

    .media-object.img-circle {
        width: 64px;
        height: 64px;
    }

    .img-circle {
        width: 48px;
        height: 48px;
    }

    .time {
        float: right;
    }

    .media {
        margin-top: 24px;
    }
</style>

app.js中,添加一行代码,注册一下这个组件

Vue.component('chat-room', require('./components/ChatRoom'));

模板中,就需要就改为,调用自定义的ChatRoom组件

@extends('layouts.app')

@section('content')
    <chat-room></chat-room>
@endsection

通过浏览器,访问http://localhost:8000/home后,聊天的界面就出来了。如果你没登录,它会让你先去登录。

GatewayWorker的配置

打开socket/GatewayWorker/Applications/YourApp/start_gateway.php,修改第21行,将协议改为websocket

$gateway = new Gateway("websocket://0.0.0.0:7272");

依照手册,修改socket/GatewayWorker/Applications/YourApp/Events.php

public static function onConnect($client_id)
{
    Gateway::sendToClient($client_id, json_encode([
        'type' => 'init',
        'client_id' => $client_id
    ]));
}

public static function onMessage($client_id, $message)
{
}

这里的type是为了区分各种情况的。例如这里的init初始化,后面还有进入房间了用户发言了退出了等等。现在就可以来运行GatewayWorker的服务了。

cd socket/GatewayWorker
php start.php start

windows用户特别注意
1. 请直接双击项目目录的start_for_win.bat启动服务,跑命令是没用的。
2. 如果发现窗口一闪而过,并且提示说php没找到什么的。那你需要配置一下php的环境变量
3. 如果console中提示连接不上,请修改js代码中连接websocket部分为ws://127.0.0.1:7272
4. 启动之前,请务必将之前学习workerman项目的服务按 ctrl + c 停止掉,不然会出现冲突。

连接websocket

先来连接一下,websocket。在js代码的最上面加上这么一行。

<script>
let ws = new WebSocket("ws://0.0.0.0:7272");

//...
</script>

当连接后,GatewayWorker就会把client_idtype发送给前端了。这个通过network是可以看到的。

然后我们到created里面,接受一下传过来的数据。

 created() {
    ws.onmessage = (e) => {
        //console.log(e.data)

        //字符串转json
        let data = JSON.parse(e.data)
        //console.log(data)

        //如果没有类型,就为空
        let type = data.type || ''
    }
},

用户绑定

传过来了一个client_id,但是这个client_id现在和我们实际登录的用户,还一点关系都没有。需要做的就是将client_id与实际登录的用户id,也就是users表中的id绑定在一起。

js

先对type做一个判断

created() {
    ws.onmessage = (e) => {
        //...

        switch (type) {
            case 'init':
                axios.post('/init', {client_id: data.client_id})
                break;

            default:
                console.log(data)
        }
    }
},

web.php

Route::post('/init', 'HomeController@init');

HomeController

use GatewayClient\Gateway;
use Auth;

class HomeController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');

        // 设置GatewayWorker服务的Register服务ip和端口
        Gateway::$registerAddress = '127.0.0.1:1238';
    }

    public function init(Request $request)
    {
        //绑定用户
        $this->bind($request);
    }

    /**
     * 绑定client_id 与 user id
    */
    private function bind($request)
    {
        $id = Auth::id();
        $client_id = $request->client_id;
        Gateway::bindUid($client_id, $id);
    }
}

提示信息

当用户进入聊天室,我希望他能有一个提示信息,就显示 某某进入了聊天室。接着在绑定用户下面,添加一个login的方法。

public function init(Request $request)
{
    //绑定用户

    //进入聊天室了
    $this->login();
}


/**
 * 提示进入聊天室
*/
private function login()
{
    $data = [
        'type' => 'say',
        'data' => [
            'avatar' => Auth::user()->avatar(),
            'name' => Auth::user()->name,
            'content' => '进入了聊天室',
            'time' => date("Y-m-d H:i:s", time())
        ]
    ];

    Gateway::sendToAll(json_encode($data));
}

因为我们聊天列表上,有用户的头像、名称、内容和时间,所以这里我们自己构建一个数组,里面包含了这些信息,还有它的typesay。然后调用Gateway的接口,将信息发送给所有人

再观察network,这样用户只要一进入聊天室,Gateway就会发送一个提示信息到前端。我们现在需要在前端接受一下数据,并且显示到聊天窗口上。

data中,先添加一个messages

data() {
    return {
        messages: [],
    }
}

当接受到数据后,就将数据pushmessages中。

case 'say':
    this.messages.push(data.data);
    break;

html部分,删除掉重复的数据,只保留一段,改为如下内容

<div class="messages">
    <div class="media" v-for="message in messages">
        <div class="media-left">
            <a href="#">
                <img class="media-object img-circle" :src="message.avatar">
            </a>
        </div>
        <div class="media-body">
            <p class="time">{{message.time}}</p>
            <h4 class="media-heading">{{message.name}}</h4>
            {{message.content}}
        </div>
    </div>
</div>

重新访问,可以看到已经有提示信息了。打开一个新的浏览器,也可以正常提示有人进入聊天室了。

Tips:
注意是另一个新的浏览器,而不是一个浏览器的新标签页。
例如我就打开了一个chrome和一个safari
因为同一个浏览器,无论你开多少个标签页,它的session都是同一个。


现在就注册ITFun.tv会员
持续关注IT界最新技术,随时更新相关课程。在线学习IT课程或直接报名参加线下武汉PHP、Web前端培训。

仅仅30秒钟就可以简单注册

会员注册(免费)
×