TCP控制器

协议规则与解析

假定,客户端与服务端都是明文传输。控制格式为

sericeName:actionName:args

实现解析器

namespace Tcp;

use EasySwoole\Core\Socket\AbstractInterface\ParserInterface;
use EasySwoole\Core\Socket\Common\CommandBean;

class Parser implements ParserInterface
{

    public static function decode($raw, $client): ?CommandBean
    {
        // TODO: Implement decode() method.
        $list = explode(":",trim($raw));
        $bean = new CommandBean();
        $controller = array_shift($list);
        if($controller == 'test'){
            $bean->setControllerClass(Test::class);
        }
        $bean->setAction(array_shift($list));
        $bean->setArg('test',array_shift($list));
        return $bean;
    }

    public static function encode(string $raw, $client): ?string
    {
        // TODO: Implement encode() method.
        return $raw."\n";
    }
}

实现一个控制服务

namespace Tcp;


use EasySwoole\Core\Socket\Response;
use EasySwoole\Core\Socket\AbstractInterface\TcpController;
use EasySwoole\Core\Swoole\ServerManager;
use EasySwoole\Core\Swoole\Task\TaskManager;

class Test extends TcpController
{
    function actionNotFound(?string $actionName)
    {
        $this->response()->write("{$actionName} not found");
    }

    function test()
    {
        $this->response()->write(time());
    }

    function args()
    {
        var_dump($this->request()->getArgs());
    }

    function delay()
    {
        $client = $this->client();
        TaskManager::async(function ()use($client){
            sleep(1);
            Response::response($client,'this is delay message at '.time());//为了保持协议一致,实际生产环境请调用Parser encoder
        });
    }

    function close()
    {
        $this->response()->write('you are goging to close');
        $client = $this->client();
        TaskManager::async(function ()use($client){
            sleep(2);
            ServerManager::getInstance()->getServer()->close($client->getFd());
        });
    }

    function who()
    {
        $this->response()->write('you fd is '.$this->client()->getFd());
    }
}

开启子服务

在EasySwooleEvent中注册。

  public static function mainServerCreate(ServerManager $server,EventRegister $register): void
    {
        // TODO: Implement mainServerCreate() method.
        $tcp = $server->addServer('tcp',9502);
        EventHelper::registerDefaultOnReceive($register,\Tcp\Parser::class,function($errorType,$clientData,$client){
            //第二个回调是可有可无的,当无法正确解析,或者是解析出来的控制器不在的时候会调用
            TaskManager::async(function ()use($client){
                sleep(3);
                \EasySwoole\Core\Socket\Response::response($client,"Bye");
                ServerManager::getInstance()->getServer()->close($client->getFd());
            });
            return "{$errorType} and going to close";
        });
    }

测试

telnet 127.0.0.1 9502
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
test
 not found
test:test
1518078988
test:args

test:delay

this is delay message at 1518079006
TARGET_CONTROLLER_NOT_FOUND and going to close
ByeConnection closed by foreign host.

HTTP往TCP推送

HTTP控制器


namespace App\HttpController;


use EasySwoole\Core\Http\AbstractInterface\Controller;
use EasySwoole\Core\Swoole\ServerManager;

class Tcp extends Controller
{

    function index()
    {
        // TODO: Implement index() method.
        $this->actionNotFound(null);
    }

    /*
     * 请调用who,获取fd
     * http://ip:9501/tpc/push/index.html?fd=xxxx
     */
    function push()
    {
        $fd = intval($this->request()->getRequestParam('fd'));
        $info = ServerManager::getInstance()->getServer()->connection_info($fd);
        if(is_array($info)){
            ServerManager::getInstance()->getServer()->send($fd,'push in http at '.time());
        }else{
            $this->response()->write("fd {$fd} not exist");
        }
    }
}

实际生产中,一般是用户TCP连接上来后,做验证,然后以userName=>fd的格式,存在redis中,需要http,或者是其他地方, 比如定时器往某个连接推送的时候,就是以userName去redis中取得对应的fd,再send。注意,通过addServer形式创建的子服务器, 以再完全注册自己的网络事件,你可以注册onclose事件,然后在连接断开的时候,删除userName=>fd对应。

results matching ""

    No results matching ""