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();

分析:

  1. 首先:客户端首先会发送一个Http的请求与服务器进行握手。握手成功后会触发onOpen事件,表示连接已就绪,onOpen函数中服务器可以得到$request对象,包含了Http握手的相关信息,如GET参数、Cookie、Http头信息等。服务器端可以设置onHandShake事件回调来手工处理WebSocket握手
  2. 然后:建立连接后客户端与服务器端就可以双向通信了。 客户端向服务器端发送信息时,服务器端触发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>fdsocketid f r a m e − > f d , 客 户 端 的 s o c k e t i d , 用 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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值