天天看点

swoole实现websocket长连接,Timer定时器

swoole

PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端/客户端。

Swoole底层内置了异步非阻塞、多线程的网络IO服务器。Swoole既支持全异步,也支持同步。

WebSocket

WebSocket服务器是建立在Http服务器之上的长连接服务器,swoole内置了WebSocket服务器端、客户端。websocket也是基于单个TCP 连接上进行全双工通讯的协议,简单的说它是支持长连接。全双工通讯就是说信息可以实现实时通讯,可以替换使用ajax轮询的方式来实现实时通讯的方法。

websocket既然是基于TCP,那么也分服务端,和客户端。

服务器代码:

 <?php                                                                                                                                                

   $server = new swoole_websocket_server("*.*.*.*", 9501);                                                                                       

   $server->on('open', function (swoole_websocket_server $server, $request) {                                                                           

     echo "server: handshake success with fd{$request->fd};\n";                              

});                                                                                                                                                                                                                                                                                                      

   $server->on('message', function (swoole_websocket_server $server, $frame) {                                                                          

     echo "receive from {$frame->fd}:{$frame->data}\n";                                                  

     $server->push($frame->fd, "this is server-test!");                                                                                  });                                                                                                                                                                                                                                                                                                     

   $server->on('close', function ($ser, $fd) {                                                                                                          

         echo "client {$fd} closed\n";                                                                                                                    

});                                                                                                                                                                                                                                                                                                    

   $server->start();  

客户端

同时打开多个浏览器模拟多client:在浏览器console里运行:

var wsl= 'ws://*.*.*.*:9501'

ws = new WebSocket(wsl);

ws.onopen    = function(){ ws.send('onopen'); };  

ws.onmessage = function(evt){console.log(evt.data);};  

ws.onclose   = function(evt){console.log('WebSocketClosed!');};  

ws.onerror   = function(evt){console.log('WebSocketError!');}; 

ws.send("client 发送的数据");

ws.close();

分析:

首先:客户端首先会发送一个Http的请求与服务器进行握手。握手成功后会触发onOpen事件,表示连接已就绪,onOpen函数中服务器可以得到$request对象,包含了Http握手的相关信息,如GET参数、Cookie、Http头信息等。服务器端可以设置onHandShake事件回调来手工处理WebSocket握手

然后:建立连接后客户端与服务器端就可以双向通信了。 客户端向服务器端发送信息时,服务器端触发onMessage事件回调,服务器端可以调用push()向某个客户端(使用$fd标识符)发送消息。

回调函数

onOpen

当WebSocket客户端与服务器建立连接并完成握手后会回调此函数。

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {

    echo "server: handshake success with fd{$request->fd}\n";

});

$request 是一个Http请求对象,包含了客户端发来的握手请求信息

onMessage

当服务器收到来自客户端的数据帧时会回调此函数。

function onMessage(swoole_server $server, swoole_websocket_frame $frame)

$frame 是swoole_websocket_frame对象,包含了客户端发来的数据帧信息。

frame−>fd,客户端的socketid,用frame−>fd,客户端的socketid,用server->push推送数据时需要用到。

$frame->data,数据内容,可以是文本内容也可以是二进制数据,可以通过opcode的值来判断。

$frame->opcode,WebSocket的OpCode类型,可以参考WebSocket协议标准文档

$frame->finish, 表示数据帧是否完整,一个WebSocket请求可能会分成多个数据帧进行发送

onMessage 回调必须被设置,未设置服务器将无法启动

Timer定时器

1.Timer定时器在实际应用中,往往会遇到需要每隔一段时间重复做一件事,比如心跳检测、订阅消息、数据库备份等工作。通常,我们会借助PHP的time()以及相关函数自己实现一个定时器,或者使用crontab工具来实现。但是,自定义的定时器容易出错,而使用crontab则需要编写额外的脚本文件,无论是迁移还是调试都比较麻烦。 

因此,Swoole提供了一个内置的Timer定时器功能,通过函数addtimer即可在Swoole中添加一个定时器,该定时器会在建立之后,按照预先设定好的时间间隔,每到对应的时间就会调用一次回调函数onTimer通知Server。 

简单示例如下:

<?php

class TimerServer

{

    private $serv;

    public function __construct() {

        $this->serv = new swoole_server("*.*.*.*", 9501);

            $this->serv->set(array(

                'worker_num' => 8,

                'daemonize' => false,

                'max_request' => 10000,

                'dispatch_mode' => 2,

                'debug_mode'=> 1 ,

            ));

            $this->serv->on('WorkerStart', array($this, 'onWorkerStart'));

            $this->serv->on('Connect', array($this, 'onConnect'));

            $this->serv->on('Receive', array($this, 'onReceive'));

            $this->serv->on('Close', array($this, 'onClose'));

            $this->serv->on('Timer', array($this, 'onTimer'));

            $this->serv->start();

    }

    public function onWorkerStart( $serv , $worker_id) {

            // 在Worker进程开启时绑定定时器

            echo "onWorkerStart\n";

            // 只有当worker_id为0时才添加定时器,避免重复添加

            if( $worker_id == 0 ) {

                $serv->addtimer(100);

                $serv->addtimer(500);

                $serv->addtimer(1000);

            }

    }

    public function onConnect( $serv, $fd, $from_id ) {

            echo "Client {$fd} connect\n";

    }

    public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {

            echo "Get Message From Client {$fd}:{$data}\n";

    }

    public function onClose( $serv, $fd, $from_id ) {

            echo "Client {$fd} close connection\n";

    }

    public function onTimer($serv, $interval) {

            switch( $interval ) {

                case 500: { // 

                    echo "500\n";

                    break;

                }

                case 1000:{

                    echo "1000\n";

                    break;

                }

                case 100:{

                    echo "100\n";

                    break;

                }

            }

    }

}

new TimerServer();

在onWorkerStart回调函数中,通过addtimer添加了三个定时器,时间间隔分别为500、1000、1500。而在onTimer回调中,正好通过间隔的不同来区分不同的定时器回调,从而执行不同的操作。 

当1000ms定时器被触发时,500ms的定时器同样会被触发,但是不能保证会在1000ms定时器前触发还是后触发,因此需要注意,定时器中的操作不能依赖其他定时器的执行结果。

心跳检测

使用Timer定时器功能可以实现发送心跳包的功能。事实上,Swoole已经内置了心跳检测功能,能自动close掉长时间没有数据来往的连接。而开启心跳检测功能,只需要设置heartbeat_check_interval和heartbeat_idle_time即可。如下:

$this->serv->set(

    array(

        'heartbeat_check_interval' => 60,

        'heartbeat_idle_time' => 600,

    )

);

其中heartbeat_idle_time的默认值是heartbeat_check_interval的两倍。 在设置这两个选项后,swoole会在内部启动一个线程,每隔heartbeat_check_interval秒后遍历一次全部连接,检查最近一次发送数据的时间和当前时间的差,如果这个差值大于heartbeat_idle_time,则会强制关闭这个连接,并通过回调onClose通知Server进程。

结合之前的Timer功能,如果我们想维持连接,就设置一个略小于如果这个差值大于heartbeat_idle_time的定时器,在定时器内向所有连接发送一个心跳包。如果收到心跳回应,则判断连接正常,如果没有收到,则关闭这个连接或者再次尝试发送。

参考文献:https://wiki.swoole.com/wiki/page/397.html

--------------------- 

作者:luke_2010 

来源:CSDN 

原文:https://blog.csdn.net/wanglin317/article/details/80681118 

继续阅读