php 使用workerman/gateway制作聊天

1,491 阅读1分钟

这次使用的是laravel框架:

1、安装workerman/gateway-worker

终端项目目录下输入:

composer require workerman/gateway-worker

2、创建workerman启动文件

php artisan make:command Workerman

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Workerman\Worker;


class Workerman extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'workerman {action} {--d}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Start a Workerman server.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
        global $argv;
        $action = $this->argument('action');
 
        $argv[0] = 'artisan workerman';
        $argv[1] = $action;
        $argv[2] = $this->option('d') ? '-d' : '';   //必须是一个-,上面定义命令两个--,后台启动用两个--
 
        $this->start();
    }

    private function start()
    {
        $this->startGateWay();
        $this->startBusinessWorker();
        $this->startRegister();
        Worker::runAll();
    }
 
    private function startBusinessWorker()
    {
        $worker                  = new BusinessWorker();
        $worker->name            = 'BusinessWorker';
        $worker->count           = 1;
        $worker->registerAddress = '127.0.0.1:1236';
        $worker->eventHandler    = \App\GatewayWorker\Events::class;
    }
 
    private function startGateWay()
    {
        $gateway = new Gateway("websocket://0.0.0.0:2346");
        $gateway->name                 = 'Gateway';
        $gateway->count                = 1;
        $gateway->lanIp                = '127.0.0.1';
        $gateway->startPort            = 2300;
        $gateway->pingInterval         = 30;
        $gateway->pingNotResponseLimit = 0;
        $gateway->pingData             = '{"type":"ping"}';
        $gateway->registerAddress      = '127.0.0.1:1236';
    }
 
    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }

}

3、创建事件监听文件

创建一个 app/GatewayWorker/Events.php 文件来监听处理 workman 的各种事件

<?php
namespace App\GatewayWorker;

use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;


class Events
{
 
    public static function onWorkerStart($businessWorker)
    {
        echo "onWorkerStart\r\n";
    }
 
    public static function onConnect($client_id)
    {
        Gateway::sendToClient($client_id, json_encode(['type' => 'onConnect', 'client_id' => $client_id]));
        // echo $client_id;
        echo $client_id."登陆成功\r\n";
    }
 
    public static function onWebSocketConnect($client_id, $data)
    {
        echo "onWebSocketConnect\r\n";
    }
 
    public static function onMessage($client_id, $message)
    {
         Gateway::sendToClient($client_id,$message);
         echo $message;
        // Gateway::sendToClient($client_id, json_encode(['type' => 'onMessage', 'client_id' => $client_id, 'name' => json_decode($message)->name]));
 
        // echo "onMessage\r\n";
    }
 
    public static function onClose($client_id)
    {
        Log::info('Workerman close connection' . $client_id);
        echo "onClose\r\n";
    }
 
}

4、测试。启动workerman服务端

php artisan workerman start --d

 在浏览器 F12 打开调试模式,在 Console 里输入

ws = new WebSocket("ws://ipaddress:2346");
    ws.onopen = function() {
        ws . send('{"name":"one","user_id":"111"}');
        ws . send('{"name":"two","user_id":"222"}');
    };
    ws.onmessage = function(e) {
        console.log("收到服务端的消息:" + e.data);
    };
    ws.onclose = function(e) {
        console.log("服务已断开" );
    }

console收到服务器端的消息.....

如在控制器里使用,需要

public function __construct()
{
        // 设置GatewayWorker服务的Register服务ip和端口,请根据实际情况改成实际值(ip不能是0.0.0.0)
    Gateway::$registerAddress = '127.0.0.1:1236';
 
}

doc2.workerman.net/

5、长链接使用代码

后端:

<?php

namespace App\Http\Controllers\chat;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use App\GatewayWorker\Events;
use GatewayWorker\Lib\Gateway;

class Chat
{
    public function __construct()
    {
        // 设置GatewayWorker服务的Register服务ip和端口,请根据实际情况改成实际值(ip不能是0.0.0.0)
        Gateway::$registerAddress = '127.0.0.1:1236';
 
    }

    public function talks()
    {
        return view('chat.talks');
    }

    public function talk(Request $request)
    {
        return view('chat.talk');
    }

    public function loginConfirm(Request $request)
    {
        $name = $request->post('name');
        if ($name) {
            $user = Redis::sAdd('name',$name);
            // if ($user) {
                return response()->json(['code'=>200]);
            // }
        }
    }

    public function getList(Request $request)
    {
        $to = $request->get('name');
        $list = Redis::sMembers('name');
        foreach ($list as $k=>$v)
        {
            $unread = Redis::hGet('unread_'.$to, $v);
            $list[$k] = array('name'=>$v, 'unread'=>$unread);
        }
        if ($list) {
            return response()->json(['code'=>200, 'list'=>$list]);
        }
    }

    public function content(Request $request)
    {

        $client_id = $request->post('client_id');
        $content = $request->post('content');
        $from = $request->post('from');
        $to = $request->post('to');
        $plus = strcmp(md5($from), md5($to));
        if ($plus == 0) {
            $res = $from.$to;
        } else if ($plus < 0) {
            $res = $from.$to;
        } else if ($plus >0) {
            $res = $to.$from;
        }
        $data = json_encode(array('from'=>$from, 'to'=>$to, 'message'=>$content, 'time'=>time()),JSON_UNESCAPED_UNICODE,true);
        $keyName = 'mes:'.$res;
        $insert = Redis::rPush($keyName, $data);
        $unread = Redis::hIncrBy('unread_'.$to,$from,1);
        if ($insert&&$unread) {
            Gateway::sendToGroup($res,$data);
            return response()->json(['code'=>200]);
        }

    }

    public function mesList(Request $request)
    {
        $from = $request->get('from');
        $to = $request->get('to');

        $plus = strcmp(md5($from), md5($to));
        if ($plus == 0) {
            $res = $from.$to;
        } else if ($plus < 0) {
            $res = $from.$to;
        } else if ($plus >0) {
            $res = $to.$from;
        }
        
        $list = Redis::lrange('mes:'.$res,-10,-1);
        foreach( $list as $k=>$v) {
            $list[$k] = json_decode($v);
        }
        Redis::hSet('unread_'.$from, $to, 0);
        $client_id = $request->get('client_id');
        Gateway::joinGroup($client_id, $res);
        return response()->json(['code'=>200, 'list'=>$list]);
    }

    public function mesRead(Request $request)
    {
        $from = $request->input('from');
        $to = $request->input('to');
        Redis::hSet('unread_'.$from, $to, 0);
    }
}

前端:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <link rel="stylesheet" type="text/css" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css">
</head>
<body style="background-color: #ececec">
<div class="page">
    <div style="width: 100%;background-color: #00AAAA" @click="back">《后退</div>
    <div v-for="list in lists">
        <div v-if="list.from==master" style="text-align: right">
            <span style="background-color: yellowgreen;">@{{ list.message }}</span>
            <span style="">@{{list.from}}</span>
        </div>
        <div v-else style="text-align: left">
            <span style="">@{{list.from}}</span>
            <span style="background-color: white;">@{{ list.message }}</span>
        </div>
    </div>
    <div style="position: fixed;bottom: 0;">
        <input type="text" v-model="content" style="height: 2rem" placeholder="填写聊天内容"/>
        <a href="javascript:" class="weui-btn weui-btn_mini weui-btn_primary" style="line-height: initial;overflow: initial" @click="send">发送</a>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script src="https://unpkg.com/axios@0.21.1/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script>
<script type="text/javascript">

    var Ca = new Vue({
        el:".page",
        data(){
            return{
                content: '',
                lists: [],
                master: '',
                client_id: ''
            }
        },
        created: function(){
            this.master = Cookies.get('name');
            ws = new WebSocket("ws://www.dzzzh.com:2346");
                ws.onmessage = function(e) {
                    console.log(e)
                    let eJson = JSON.parse(e.data)
                    if (eJson.type == "onConnect") {
                        // Cookies.set('client_id', eJson.client_id)
                        Ca.client_id = eJson.client_id
                        Ca.mesList()
                    }
                    if (eJson.message){
                        Ca.lists.push(eJson)
                        Ca.read()
                    }
                    console.log("收到服务端的消息:" + e.data);
                };
        },
        methods:{
            back(){
                window.location.href="{{url('/talks')}}"
            },
            send(){
                axios.post("{{url('/api/content')}}",{
                    from: "{{$_GET['name']}}",
                    to: "{{$_GET['to']}}",
                    content: this.content,
                    client_id: this.client_id
                }).then(function(res){
                    if (res.data.code === 200) {
                        Ca.content = '';
                    }
                }).catch(function(err){
                    console.error(err)
                })

            },
            mesList(){
                axios.get("{{url('/api/mesList')}}",{params:{
                    from:"{{$_GET['name']}}",
                    to:"{{$_GET['to']}}",
                    client_id: Ca.client_id
                }}).then(function(res){
                    if (res.data.code === 200) {
                        Ca.lists = res.data.list
                    }
                }).catch(function(err){
                    console.error(err)
                })
            },
            read(){
                axios.put("{{url('/api/mesRead')}}",{params:{
                    from: "{{$_GET['name']}}",
                    to: "{{$_GET['to']}}",
                }}).then(function(res){
                    console.log(res)
                }).catch(function(err){
                    console.log(err)
                })
            }
        }
    })
</script>
</body>
</html>