最近在看Workerman,发现这是一个真的很好用的东西。
文档相当丰富,而且大神walker很贴心,有问必答。
下面记一下一个简单的基于GatewayWorker框架写的websocket的程序。
传统的websocket使用方式都是
1.客户端像服务器发送http请求,并发起websocket链接请求。
然后php框架调用GatewayWorker进行uid和websocket链接信息进行绑定。
2、客户端发送http请求交由php框架进行业务逻辑处理,返回给客户端HTTP Response。
再交由GatewayWorker把需要websocket通信的数据推送给其他用户。
例如Workerman推荐的就是使用此方式,可以很简单的集成一个类似客服系统,聊天系统。
看到这里我就想了一个问题,php能做什么?简单的curd?日复一日的在公司搬砖码代码,进行所谓的api开发?
Wokerman是一个基于PHP-CGI开发的多线程 无阻赛I/O的东西(我的理解哈)
那就可以用来做的事情就很多了
例如做一个简单的休闲游戏服务器???比如农场???
这种对实时通讯要求不太高的休闲小游戏就完全可以用PHP做服务器的开发嘛
再比如升级一点难度,做个完全实时双向通信的MMROPG游戏?
下面贴一下我简单的认识。
下载GatewayWorker
其他的代码都不需要改,注意改一下start_gateway.php里面的东西。
这里就先把"text://0.0.0.0:8282"
修改为websocket协议"websocket://0.0.0.0:8282"
然后PHP start.php 开启workerman服务
找个webscoket测试工具就能直接连接上了。。
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://0.0.0.0:8282");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数
$gateway->count = 4;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1238';
// 心跳间隔
//$gateway->pingInterval = 10;
// 心跳数据
//$gateway->pingData = '{"type":"ping"}';
/*
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
$connection->onWebSocketConnect = function($connection , $http_header)
{
// 可以在这里判断连接来源是否合法,不合法就关掉连接
// $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
{
$connection->close();
}
// onWebSocketConnect 里面$_GET $_SERVER是可用的
// var_dump($_GET, $_SERVER);
};
};
*/
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
修改Evnets.php
引入一个简单的路由类
use \Core\Router
并在onMessage回调函数中把所有的信息交给Router::run()来进行业务逻辑处理
Router::run();就是我们自定义的业务层的入口了
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* 用于检测业务代码死循环或者长时间阻塞等问题
* 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
* 然后观察一段时间workerman.log看是否有process_timeout异常
*/
//declare(ticks=1);
use \GatewayWorker\Lib\Gateway;
use \Core\Router;
/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
* onConnect 和 onClose 如果不需要可以不用实现并删除
*/
class Events
{
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
// 向当前client_id发送数据
Gateway::sendToClient($client_id, "Hello $client_id\r\n");
// 向所有人发送
Gateway::sendToAll("$client_id login\r\n");
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message)
{
// 向所有人发送
$data=Router::run($message);
Gateway::sendToCurrentClient($data);
//Gateway::sendToAll("$client_id said $message $data \r\n");
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
// 向所有人发送
GateWay::sendToAll("$client_id logout\r\n");
}
}
再来看看我们的Router类,其实就是一个简单的路由处理类。
通过比对客服端提交的json数据,再把请求数据交给具体的Controller来进行处理。
在路由规则中我们定义了test路由到Test控制器的test方法来进行业务处理。
而这时客服端提交的数据就应该是
{"route":"test"}就可以直接路由了。关于其他参数我们甚至可以
{"route":"test","user":{"uid":1,"token":2},"data":{"pk":true,"info":"list"}}
这里面可以传递token,等等等等。。。。。具体的交给我们的控制器操作进行了
甚至通过Controller 的Method返回来的数据我们可以再进行一次封装,比如数据参数不对啊,把客户端踢下线啊,我们经过自定义一个简单的type或者status然后switch一下,再来返回给客户端什么数据都是可以的。
<?php
/**
* Created by PhpStorm.
* User: 陈具龙
* QQ:823380606
* Date: 2019/7/20
* Time: 23:05
*/
namespace Core;
/** Core核心路由类
* Class Router
* @package Core
*/
class Router{
//配置规则路由
public static $rules=array(
'test'=>'Test@test'
);
public static function run($parm){
$parm=json_decode($parm,true);
if (key_exists($parm['route'],self::$rules)){
$a=explode('@',self::$rules[$parm['route']]);
$Controller='App\\Controller\\'.$a[0];
$Method=$a[1];
return $Controller::$Method();
// {"route":"test"}
}else {
//return json_encode($parm);
// $arr=array(
// 'route'=>'test',
// 'user'=>array(
// 'uid'=>1,
// 'token'=>2,
// ),
// 'data'=>array(
// 'pk'=>true,
// 'info'=>'list'
// )
// );
return false;
}
}
}
现在MVC框架
C就是我们自定义的Controller
V就是GatewayWorker
就差M了。M引用一下thinkphp的orm
composer require topthink/think-orm
具体看https://github.com/top-think/think-orm
经过这一系列的操作,我们就相当于自己做了一个websocket的MVC框架了。
我们甚至可以把路由写得复杂一点,加入,消息加解密啊,中间件啊,命名空间啊,数据验证啊。我相信一个入门了两年以上的PHPer应该能很轻松自己把这些东西写出来吧。然后再统一一下Controller的返回数据,再交由Core进行统一调度,和前端同学统一一下格式,是不是就是像开发api接口一样简单?
而且用php做这方面的开发,性能比也不差,主要就是快啊,作为一个不想学习其他语言又拥有开发小游戏的同学来说真的是福音啊。下一步就是和node.js来做一个性能比较了,看看是不是真的可以这么用!!!!!