Laravel下Workerman实现websocket长连接

Laravel下Workerman实现websocket长连接

  • 安装workerman
composer require workerman/workerman -vvv 
  • 创建laravel自定义命令
php artisan make:command Workerman

执行这段命令后会在\app\Console\Commands文件夹下生成Workerman.php文件

  • Workerman.php文件代码 :
<?php
    namespace App\Console\Commands;
    use Workerman\Worker;
    use Illuminate\Console\Command;
    class Workerman extends Command {
        private $server;
        // -d 是否以debug方式运行
        protected $signature = 'workerman {action} {-d?}';
        protected $description = 'Start a Workerman server.';
        
        public function __construct() {
            parent::__construct();
            
        }

        /** * Execute the console command. * * @return mixed */
        public function handle() {
            global $argv;

            $context = array(
                // 更多ssl选项请参考手册 http://php.net/manual/zh/context.ssl.php
                'ssl' => array(
            
                    // 请使用绝对路径
            
                    'local_cert'                 => '/www/server/panel/vhost/cert/www.example.com/fullchain.pem', // 也可以是crt文件
            
                    'local_pk'                   => '/www/server/panel/vhost/cert/www.example.com/privkey.pem',
            
                    'verify_peer'                => false,
            
                    // 'allow_self_signed' => true, //如果是自签名证书需要开启此选项
            
                )
            
            );
        
            // 这里设置的是websocket协议(端口任意,但是需要保证没被其它程序占用)
        
            $worker = new Worker('websocket://0.0.0.0:2000', $context);
            
            // 设置transport开启ssl,websocket+ssl即wss
            
            $worker->transport = 'ssl';
            
            // $worker->onMessage = function($con, $msg) {
        
            //     $con->send('ok');
                
            // };
            $this->server = $worker;
            // $this->server = new Worker("websocket://0.0.0.0:2000");
            $arg = $this->argument('action');
            $argv[1] = $argv[2];
            $argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';

            switch ($arg) {
                case 'start':
                    $this->start();
                    break;
                case 'stop':
                    $this->stop();
                    break;
                case 'restart':
                    $this->restart();
                    break;
                case 'reload':
                    $this->reload();
                    break;
            }
        }
        private function start() {
            // 启动4个进程对外提供服务
            $this->server->count = 4;
            $handler = \App::make('handlers\WorkermanHandler');
             // 连接时回调
            $this->server->onConnect = [$handler, 'onConnect'];
             // 收到客户端信息时回调
            $this->server->onMessage = [$handler, 'onMessage'];
             // 进程启动后的回调
            $this->server->onWorkerStart = [$handler, 'onWorkerStart'];
             // 断开时触发的回调
            $this->server->onClose = [$handler, 'onClose'];
             // 运行worker
            Worker::runAll();
        }

        private function stop() {
            $worker = $this->server;
            // 设置此实例收到reload信号后是否reload重启
            $worker->reloadable = false;
            $worker->onWorkerStop = function($worker)
            {
                echo "Worker reload...\n";
            };
            // 运行worker
            Worker::runAll();
        }
        private function restart() {
            $worker = $this->server;
            // 设置此实例收到reload信号后是否reload重启
            $worker->reloadable = true;
            $worker->onWorkerStart = function($worker)
            {
                echo "Worker restart...\n";
            };
            // 运行worker
            Worker::runAll();
        }
        private function reload() {
            $worker = $this->server;
            // 设置此实例收到reload信号后是否reload重启
            $worker->reloadable = false;
            $worker->onWorkerStart = function($worker)
            {
                echo "Worker reload...\n";
            };
            // 运行worker
            Worker::runAll();
        }
    }`

  • workerman引用的执行文件

app\Console\Commands\handlers\WorkermanHandler.php文件

<?php
    namespace handlers;
    use Workerman\Lib\Timer;
    use Illuminate\Support\Facades\Redis;
    use App\Log\LogController;

    // 心跳间隔10秒
    define('HEARTBEAT_TIME',100);

    class WorkermanHandler {
        const check = 0;

        // 处理客户端连接
        public function onConnect($connection) {
            echo "链接成功";
        }
        public function editLog($function,$param,$res){
            $log=new LogController();
            // if($this->log == '1'){
                $log->Success('接口:'.$function.';上传参数如下:'.json_encode($param).';返回参数:'.json_encode($res));
            // }
        }
        // public function onMessage($connection, $data) {
        //     $connection->send($data);
        // }
        // 处理客户端消息
        public function onMessage($connection, $data) {
            $this->editLog($function='onMessage',$data,'');
            //当上来数据获取最后上传数据的时间
            $connection->lastMessageTime = time();
            // $connection->send($data);
            // 心跳包
            if($data!='{"t2yp3e":"213213","sae1acr213et":"sadasdas"}'){
                //RSA解密
                if(self::check == 1){
                        $myfile = fopen("rsa/rsa_private_key.pem", "r") or die("Unable to open file!");
                        $private_key = fread($myfile,filesize("rsa/rsa_private_key.pem"));
                        $pi_key =  openssl_pkey_get_private($private_key);
                        $encrypted = str_replace(' ', '+',$data);
                        $decrypted = ""; 
                        // 解密
                        openssl_private_decrypt(base64_decode($encrypted),$decrypted,$pi_key);//私钥解密
                        fclose($myfile);
                        //解密内容为空
                        if($decrypted==''){
                            $connection->send('验证失败'); 
                            
                        }
                        // $connection->send($decrypted);
                        //数组化参数
                        $data = json_decode($decrypted); 
                        $this->editLog($function='onMessage',$data,'');
                    
                }else{
                    //测试时候用
                    $connection->send($data); 
                    $data = json_decode($data);
                }
            }else{
                $connection->send($data); 
                $data='';
            }
            
            if($data!=''){
                //执行你想做的事情
            }
            
            //故障代码
            if(!empty($data->code)){
                //执行你想做的事情
            }
            
            // 记录速度等数据(详情被激活)可忽略
            if(!empty($data->bluetooth)){
                $key_detail = 'detail'.$data->bluetooth;
                $detail = Redis::get($key_detail);
                if($detail){
                    //曲线图的数据
                    $key_curve = $data->bluetooth.'curve';
                    $curve = Redis::get($key_curve);
                    if(!empty($curve)){
                        $curve = unserialize($curve);
                        // echo json_encode($curve);
                        //数组满多少个元素会自动清除第一个元素
                        // if(count($curve)>5){
                        //     array_shift($curve);
                        // }
                         array_push($curve,['speed'=>$data->speed??'0','voltage'=>$data->voltage??'0','electric_current'=>$data->current??'0','output_power'
                        =>$data->outpower??'0','Motherboardtemperature'=>$data->mTemperature??'0','Motortemperature'=>$data->eTemperature??'0','createtime'=>date('Y-m-d H:i:s',time())]);
                        Redis::setex($key_curve, 10, serialize($curve));
                    }else{
                        $curve = array();
                         array_push($curve,['speed'=>$data->speed??'0','voltage'=>$data->voltage??'0','electric_current'=>$data->current??'0','output_power'
                        =>$data->outpower??'0','Motherboardtemperature'=>$data->mTemperature??'0','Motortemperature'=>$data->eTemperature??'0','createtime'=>date('Y-m-d H:i:s',time())]);
                        Redis::setex($key_curve, 10, serialize($curve));
                    }
                }else{
                    echo 'detail is not';
                }

                $open = Redis::get($data->bluetooth.'open');
                if($open == '1'){
                    $export = Redis::get($data->bluetooth.'export');
                    if(!empty($export)){
                        $export = unserialize($export);
                         array_push($curve,['speed'=>$data->speed??'0','voltage'=>$data->voltage??'0','electric_current'=>$data->current??'0','output_power'
                        =>$data->outpower??'0','Motherboardtemperature'=>$data->mTemperature??'0','Motortemperature'=>$data->eTemperature??'0','createtime'=>date('Y-m-d H:i:s',time())]);
                        Redis::setex($data->bluetooth.'export', 300, serialize($export)); 
                    }else{
                        $export = array();
                         array_push($curve,['speed'=>$data->speed??'0','voltage'=>$data->voltage??'0','electric_current'=>$data->current??'0','output_power'
                        =>$data->outpower??'0','Motherboardtemperature'=>$data->mTemperature??'0','Motortemperature'=>$data->eTemperature??'0','createtime'=>date('Y-m-d H:i:s',time())]);
                        Redis::setex($data->bluetooth.'export', 300, serialize($export)); 
                    }
                }
                
            }

            // 激活详情(详情请求websocket时触发)mTemperature
            if(!empty($data->bluetooth)){
                $key_detail = 'detail'.$data->bluetooth;//为1时,曲线图存储速度等数据
                Redis::setex($key_detail,'10', 1);
                
               // 获取曲线图的速度等数据
                $key_curve = $data->bluetooth.'curve';
                $curve = Redis::get($key_curve);
                $connection->send(json_encode(unserialize($curve)));   
                
                
            }

            if(!empty($id)){
                if(!empty($data->code)&&$data->code!=''){
                    \DB::table('dlc_websocket')->where('id',$id)->update(['code'=>$data->code]);
                }
                //id赋值
                $connection->id = $id;
            }
            // }
        }

        // 处理客户端断开
        public function onClose($connection) {
            echo "connection closed from ip {$connection->getRemoteIp()}\n";
        }

        public function onWorkerStart($worker) {
            Timer::add(1, function () use ($worker) {
                $time_now = time();
                foreach ($worker->connections as $connection) {
                    // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
                    if (empty($connection->lastMessageTime)) {
                        $connection->lastMessageTime = $time_now;
                        continue;
                    }
                    // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
                    if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                        if($connection->id){
                            \DB::table('dlc_websocket')->where('id',$connection->id)->delete();
                            // \DB::table('dlc_websocket')->where('id',$connection->id)->update(['status'=>0]);
                        }
                        //断开后的回调
                        echo "Client ip {$connection->getRemoteIp()} timeout!!!\n"; $connection->close();
                    }
                }
            });
            
        }
    }

第一个文件是配置workerman的基本信息,可以配置https,长连接的会是wss而不是ws。第二个文件是用于接收到数据进行处理,比如用于存储,前端页面画图展示等。

  • 启动workerman
php artisan Workerman start d
  • 关闭workerman
php artisan Workerman stop d
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Android中实现WebSocket长连接,可以通过以下步骤来实现: 1. 导入WebSocket库:首先,需要在Android项目的build.gradle文件中添加WebSocket库的依赖项。例如,在dependencies块中添加以下代码: ```java implementation 'org.java-websocket:Java-WebSocket:1.5.1' ``` 2. 创建WebSocket客户端:在需要使用WebSocket长连接的地方,创建一个WebSocket客户端对象。例如,可以在Activity或Fragment中创建WebSocketClient对象。 ```java WebSocketClient mWebSocketClient = new WebSocketClient(URI.create("ws://your_server_url"), new Draft_17()) { // WebSocket事件回调方法 @Override public void onOpen(ServerHandshake serverHandshake) { // 连接成功后的处理逻辑 } @Override public void onMessage(String message) { // 接收到服务器发送的消息时的处理逻辑 } @Override public void onClose(int code, String reason, boolean remote) { // 连接关闭时的处理逻辑 } @Override public void onError(Exception ex) { // 连接发生错误时的处理逻辑 } }; ``` 3. 连接WebSocket服务器:调用WebSocketClient对象的connect()方法来连接WebSocket服务器。 ```java mWebSocketClient.connect(); ``` 4. 发送和接收消息:可以使用WebSocketClient对象的send()方法发送消息,并在onMessage()方法中接收服务器发送的消息。 ```java mWebSocketClient.send("Hello, Server!"); ``` 5. 关闭WebSocket连接:当不再需要长连接时,可以调用WebSocketClient对象的close()方法来关闭WebSocket连接。 ```java mWebSocketClient.close(); ``` 注意:以上代码只是简单示例,实际使用时可能需要根据具体需求进行适当的改进和处理,例如添加重连机制、处理心跳包等。 ### 回答2: Android实现WebSocket长连接可以使用Java开发,具体步骤如下: 1. 引入相应的WebSocket依赖库,如Java-WebSocket库。 2. 创建一个WebSocket连接类,继承自WebSocketClient类,并实现其中的各个回调方法。 3. 在连接类中,重写onOpen()方法,在该方法中进行WebSocket连接的初始化和握手操作。 4. 重写onMessage()方法,用于接收服务器发送的消息,可以在此方法中处理收到的消息。 5. 重写onClose()方法,处理WebSocket连接关闭的逻辑。 6. 重写onError()方法,处理WebSocket连接发生错误的逻辑。 7. 在需要使用WebSocket长连接的地方,创建WebSocket连接对象,传入服务器的WebSocket地址和端口号等参数。 8. 调用WebSocket连接对象的connect()方法,发起WebSocket连接。 9. 可以通过WebSocket连接对象的send()方法发送消息给服务器。 10. 在连接不需要时,调用WebSocket连接对象的close()方法关闭WebSocket连接。 总结:通过引入相应的WebSocket依赖库,并创建WebSocket连接类来实现WebSocket长连接,可以实现Android应用与服务端的实时通讯。在连接的各个回调方法中,可以处理接收到的消息和连接关闭等事件。使用WebSocket连接对象的connect()方法发起连接,send()方法发送消息,close()方法关闭连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值