Hyperf转发WebScoket

感觉Hyperf虽然网上资料很少,但是文档写得比较详细,而且还有对应的视频教程,比较好上手

像ws转发,这种看下文档基本也能自己实现,主要要用到的组件有WebSocket 服务WebSocket 协程客户端自定义进程

先创建一个普通的WebSocket 应用,详细步骤可参照官方文档

这里要主要要提到的是把等下需要转发的信息先缓存到REDIS里面,附上代码:

<?php
declare(strict_types=1);

namespace App\Controller;

use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\Utils\ApplicationContext;

class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
    public function onMessage(WebSocketServer $server, Frame $frame): void
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Redis::class);
        $getData = $frame->data;
        $getData = json_decode($getData,true);

        if(isset($getData['type'])){
            $type = $getData['type'];
            // echo $type;
            $roomId = "All";
            switch ($type) {
                case 'sub':
                    $redis->sAdd('roomInfo_'.$roomId, $frame->fd);
                    $redis->expire('roomInfo_'.$roomId, 7200);
                    break;
                case 'unsub':
                    $redis->sRem('roomInfo_'.$roomId, $frame->fd);
                    break;
                case 'login':
                    if(isset($getData['user_id']) && $getData['user_id']){
                        $uid = $getData['user_id'];
                        $server->bind($frame->fd,$uid);
                        $result = $this->rpcService->setinfo($uid,['is_online'=>1]);
                    }
                    break;
                case 'logout':
                    if(isset($getData['user_id']) && $getData['user_id']){
                        $uid = $getData['user_id'];
                        // $redis->sRem('online_list',$uid);
                        $result = $this->rpcService->setinfo($uid,['is_online'=>0]);
                    }
                    break;
                
                default:
                    # code...
                    break;
            }
        }
        // $server->push($frame->fd, 'Recv: ' . $frame->data);
    }

    public function onClose(Server $server, int $fd, int $reactorId): void
    {
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Redis::class);
        $result = $server->getClientInfo($fd);
        if(isset($result['uid']) && $result['uid']){
            $uid = $result['uid'];
            $result = $this->rpcService->setinfo($uid,['is_online'=>0]);
        }
        // echo $fd.' Closed';
    }

    public function onOpen(WebSocketServer $server, Request $request): void
    {
        // $server->push($request->fd, $request->fd.' Connected');
    }
}

然后创建一个自定义进程ScoketProcess并创建客户端对象,如何添加自定义进程和ws客户端均参照官方文档

以下附上代码(注:代码因为去掉了一些业务逻辑,可能会出现一点问题,但是按这个逻辑经测是可行的):

<?php
declare(strict_types=1);

namespace App\Process;

use App\Model\Model;
use Hyperf\Process\AbstractProcess;
use Hyperf\Process\Annotation\Process;
use Hyperf\WebSocketClient\ClientFactory;
use Hyperf\WebSocketClient\Frame;

use Hyperf\Server\Server;
use Hyperf\Server\ServerFactory;
use Hyperf\Utils\ApplicationContext;

/**
 * @Process(name="scoket_process")
 */
class ScoketProcess extends AbstractProcess
{
    public function handle(): void
    {
        $api = config("shadow.api");
        $host = $api['url'];
        //获取授权TOKEN
        $url = $host.'/login?account='.$api['username'].'&&passwd='.$api['password'];
        $response = GetData($url,'',"GET"); 
        $json = json_decode((string)$response,true);
        if(!$json) return;      
        if($json['code'] != '200') return;
        if($json['code'] == '200'){
            $Model = new Model();
            $container = ApplicationContext::getContainer();
            $redis = $container->get(\Redis::class);
            $token = $json['token'];
            $parmas = "token=".$token;
            //获取WS地址和端口
            $url = $host.'/getsign?'.$parmas;       
            $response = GetData($url,'',"GET");     
            $json = json_decode($response,true);    
            if(!$json) return;  
            if($json['code'] != '200') return;  
            $ClientFactory = new ClientFactory;
            $data = $json["data"];
            // 对端服务的地址,如没有提供 ws:// 或 wss:// 前缀,则默认补充 ws://
            $host = $data['ip'].':'.$data['port']."?sign=".$data['sign'];
            // echo $host.PHP_EOL;
            // 通过 ClientFactory 创建 Client 对象,创建出来的对象为短生命周期对象
            $client = $ClientFactory->create($host);
            // 向 WebSocket 服务端发送消息
            // 获取服务端响应的消息,服务端需要通过 push 向本客户端的 fd 投递消息,才能获取;以下设置超时时间 2s,接收到的数据类型为 Frame 对象。
            while (true) {
                /** @var Frame $msg */
                $msg = $client->recv(2);
                if($msg){
                    // var_dump($msg->data);
                    if(isset($msg->data)){
                        $data = json_decode($msg->data,true);
                        if(isset($data['type'])){
                            switch ($data['type']) {
                                case 'do':
                                    foreach ($data['data'] as $key => $value) {
                                        $update = [
                                            'status' => $value['status'],
                                            'utime' => time()
                                        ];
                                        $result = $Model->where('mid',$value['rid'])->update($update);
                                        $update['mid'] = $value['rid'];
                                    }
                                    //此处重点注释下,获取服务
                                    $server = $this->container->get(ServerFactory::class)->getServer()->getServer();
                                    $fdList = $redis->sMembers('roomInfo_'.$value['rid']);
                                    foreach($fdList as $v){
                                        if(!empty($v)){
                                            $result = $server->push((int)$v, json_encode($update));
                                            if(!$result){
                                                $redis->sRem('roomInfo_'.$value['rid'], $v);
                                            }
                                        }
                                    }                                                        
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                }
            }
        }
    }

    // public function isEnable(): bool
    // {
    //     // 不跟随服务启动一同启动
    //     return false;   
    // }
}

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值