swoole_http_server
类的监听事件列表相比swoole_server
额外接受1种新的事件类型onRequest
:/**
* 注册事件回调函数,与swoole_server->on相同。swoole_http_server->on的不同之处是:
*
* * swoole_http_server->on不接受onConnect/onReceive回调设置
* * swoole_http_server->on 额外接受1种新的事件类型onRequest
*
* 事件列表
*
* * onStart
* * onShutdown
* * onWorkerStart
* * onWorkerStop
* * onTimer
* * onConnect
* * onReceive
* * onClose
* * onTask
* * onFinish
* * onPipeMessage
* * onWorkerError
* * onManagerStart
* * onManagerStop
* WebSocket
* * onOpen
* * onHandshake
* * onMessage
*
*
* $http_server->on('request', function(swoole_http_request $request, swoole_http_response $response) {
* $response->end("hello swoole
");
* })
*
*
* 在收到一个完整的Http请求后,会回调此函数。回调函数共有2个参数:
*
* * $request,Http请求信息对象,包含了header/get/post/cookie等相关信息
* * $response,Http响应对象,支持cookie/header/status等Http操作
*
*
* !! $response/$request 对象传递给其他函数时,不要加&引用符号
*
* @param string $event
* @param callable $callback
*/
public function on($event, $callback)
{
}
websocketServer
虽然只显示提供了onOpen
,onHandshake
, onMessage
三个事件, 但是websocketServer
是继承自httpServer
的, 所以其可以onRequests
。
pecl install swoole
php artisan make:command swoole
, 该文件如下:fire();
}
/*
* 获取命令行参数
*/
protected function getArguments(){
return array(
'action',InputArgument::REQUIRED,'start|stop|restart'
);
}
/*
* 执行相应命令功能
*/
public function fire(){
$arg = $this->argument('action');
switch($arg){
case 'start':
$this->line('启动websocket服务, 监听端口: ' . config('swoole.port'));
$this->start();
break;
case 'stop':
$this->line('停止websocket服务...');
break;
case 'restart':
$this->line('重启websocket服务...');
break;
}
}
/*
* 开启swoole服务
*/
public function start(){
$this->server = new Server(config('swoole.host'), config('swoole.port'));
$this->server->set(
array(
'worker_num' => config('swoole.worker_num'),
'daemonize' => config('swoole.daemonize'),
'log_file' => config('swoole.log_file'),
'max_request' => config('swoole.max_request'),
'dispatch_mode' => config('swoole.dispatch_mode'),
'debug_mode' => config('swoole.debug_mode'),
'max_connection' => config('swoole.max_connection')
)
);
$handler=SwooleHandler::getInstance();
$this->server->on('open', [$handler,'onOpen']);
$this->server->on('workerstart', [$handler,'onWorkStart']);
$this->server->on('message', [$handler,'onMessage']);
// $this->server->on('close', [$handler,'onClose']);
// websocket类是基于http的, 所以有http回调
$this->server->on('request', function ($request, $response) use($handler){
$handler->onRequest($this->server, $request, $response);
});
$this->server->start();
}
/*
* 停止swoole服务
*/
public function stop()
{
// kill -QUIT xxx.pid 平滑终止
}
/*
* 重启swoole服务
*/
public function restart()
{
// 可通过kill -USR2 xx.pid 来平滑重启
}
}
$this->server->on('request', function ($request, $response) use($handler){
$handler->onRequest($this->server, $request, $response);
});
namespace App\Handlers;
/*
* websocket事件监听处理
*/
use App\Logic\Api\TaskLogic;
use Illuminate\Support\Facades\Redis;
use App\Logic\Api\WebsocketLogic;
class SwooleHandler
{
private static $_instance; //保存类实例的私有静态成员变量
private $redis;
public function __construct()
{
}
//定义私有的__clone()方法,确保单例类不能被复制或克隆
private function __clone() {}
//对外提供获取唯一实例的方法
public static function getInstance()
{
//检测类是否被实例化
if ( ! (self::$_instance instanceof self) ) {
self::$_instance = new SwooleHandler();
}
return self::$_instance;
}
/**
* 监听websocket启动事件
* @param $server
* @param $request
*/
public function onWorkStart($server, $request)
{
$redis = Redis::connection();
$server->redis = $redis;
}
/**
* 监听websocket连接事件
* @param $server
* @param $request
*/
public function onOpen($server, $request)
{
$params = $request->get;
$is_validate = is_validate_sign($params); // 校验websocket客户端签名
if ($is_validate && array_key_exists('canteen_id', $params))
{
$canteen_id = $params['canteen_id'];
// 连接成功之后将websocket fd缓存起来
WebsocketLogic::saveFd($canteen_id, $request->fd, $server->redis);
$response = [
'status' => 1,
'msg' => '连接成功',
'code' => 0
];
$server->push($request->fd, json_encode($response));
return;
}
else {
$response = [
'status' => 0,
'code' => 40000,
'msg' => '校验签名不通过',
];
$server->push($request->fd, json_encode($response));
$server->close($request->fd);
return;
}
}
/**
* 监听消息事件
* @param $server
* @param $frame
*/
public function onMessage($server, $frame)
{
// 省略
}
/**
* 监听http请求事件
* @param $server
* @param $request
* @param $response
*/
public function onRequest($server, $request, $response)
{
$response->header('content-type', 'application/json', true);
$remote_addr = $request->server['remote_addr'];
if ($remote_addr != '127.0.0.1') {
$resp = [
"error" => 1001,
"msg" => "非法调用",
];
$response->end(json_encode($resp));
return;
}
$post_data = $request->post;
// 从redis获取websocket fd
$canteen_id = $post_data['canteen_id']; // 食堂ID
$fd = WebsocketLogic::getFd($canteen_id, $server->redis);
$websocket_status = $server->connection_info($fd)['websocket_status'];
if ($websocket_status != 3) // 判断该websocket通道是否为连接成功的状态
{
$resp = [
"errno" => 50000,
"msg" => "客户端未连接",
];
$response->end(json_encode($resp));
return;
}
$response->end(json_encode($resp));
}
}
webscoket
成功建立连接后将fd
缓存起来, onRequest
发送推送数据给客户端时根据缓存起来的fd
进行消息推送。
通过http请求发送websocket数据:
/*
* 发送数据给websocket服务端
*/
function push_data_to_ws($data)
{
$url = 'http://' . config('swoole.host') . ':' . config('swoole.port');
$headers = array('Accept' => 'application/json');
$request = Requests::post($url, $headers, $data);
$resp = $request->body;
return json_decode($resp, true);
}
使用了模仿python
的requests
库实现的Requests For PHP, 使用curl实现也是一样的。
为需要推送数据至websocket客户端的业务写个接口,在其中调用push_data_to_ws
方法即可。