rpc client and server of PHP

[TOC]

rpc client and server of PHP

There are many solutions for PHP rpc, and many open rpc frameworks based on PHP code can be seen on composer.

There are also many rpc frameworks based on C PHP extension, such as swoole, Yar and grpc.

The ready-made framework is very simple to use. Just install and call it directly, I won't say it. Let's just say that rpc service written in PHP code is used.

The following simple case is very simple, which is such a structure.

~/Desktop/rpcDemo ⌚ 18:45:43
$ tree
.
├── rpcClient.php
├── rpcServer.php
└── service
    └── test.php

1 directory, 3 files

Simple description of rpc Protocol

rpc server of simple pure PHP code

<?php

/**
 * rpc service
 */
class rpcServer
{
    protected $server;

    /**
     * Return data function.
     *
     * @param [type] $data
     *
     * @return mixed
     */
    public function response($data)
    {
        if (is_array($data)) {
            $data = json_encode($data);
        }
        echo $data.PHP_EOL;

        return;
    }

    /**
     * Create service function.
     *
     * @param [string] $host link
     * @param [int]    $port Port number
     */
    public function instance($host, $port)
    {
        //Create a Socket service
        //AF INET IPv4 network protocol
        //Sock "stream provides a sequential, reliable, full duplex, connection based byte stream. Support data transmission flow control mechanism. The TCP protocol is based on this streaming socket.
        //Sol? TCP TCP and UDP correspond to sol? TCP and sol? UDP
        if (($this->server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
            $this->response('socket_create() Execution failure:'.socket_strerror(socket_last_error()));
        }

        //Set port reuse
        if (!socket_set_option($this->server, SOL_SOCKET, SO_REUSEADDR, 1)) {
            $this->response('socket_set_option() Execution failure:'.socket_strerror(socket_last_error()));
        }
        //Binding port
        if (socket_bind($this->server, $host, $port) < 0) {
            $this->response('socket_set_option() Execution failure:'.socket_strerror(socket_last_error()));
        }
        //Monitor port
        if ((socket_listen($this->server, 3)) < 0) {
            $this->response('socket_listen() Execution failure:'.socket_strerror(socket_last_error()));
        }
    }

    /**
     * Constructor function.
     *
     * @param [string] $host link
     * @param [int]    $port Port number
     * @param [string] $path Requested method path
     */
    public function __construct($host, $port, $path)
    {
        // Determine whether RPC service directory exists
        $realPath = realpath(__DIR__.$path);
        if (!is_dir($realPath)) {
            $this->response('path Parameter error,directory does not exist:'.$path);
        }
        //Activate server
        $this->instance($host, $port);
        //Execution operation
        $this->processing($realPath);
    }

    /**
     * processing function.
     *
     * @param [string] $realPath Service code path
     */
    public function processing($realPath)
    {
        do {
            //Start operation
            $client = socket_accept($this->server);
            if ($client) {
                // One time read
                $buffer = socket_read($client, 1024);
                echo 'Data received within the length of client 1024: '.PHP_EOL.$buffer.PHP_EOL;

                //Regular judgment of data submitted by client
                $classData = preg_match('/Rpc-Class:\s(.*);\n/i', $buffer, $class);
                $methodData = preg_match('/Rpc-Method:\s(.*);\n/i', $buffer, $method);
                $paramsRet = preg_match('/Rpc-Params:\s(.*);\n/i', $buffer, $params);
                if ($classData && $methodData) {
                    $class = $class[1];
                    $method = $method[1];
                    $params = $params[1];
                    if (!empty($params)) {
                        $params = json_decode($params, true);
                        if (is_array($params)) {
                            $params = implode(',', $params);
                        }
                    }

                    $file = $realPath.'/'.$class.'.php';  // Class file needs to be consistent with class name
                    $data = ''; // Execution result as return value
                    // Judge whether the class file exists
                    if (file_exists($file)) {
                        // Import class file
                        include $file;
                        // Instantiation class is used to export or extract detailed information about classes, methods, properties, parameters, etc., including comments.
                        $refObj = new ReflectionClass($class);
                        // Determine whether the specified method exists in the object
                        if ($refObj->hasMethod($method)) {
                            // Execute the object method
                            $refMethod = $refObj->getMethod($method);
                            if (!empty($params)) {
                                //Passing parameters to a method
                                $data = $refMethod->invokeArgs($refObj->newInstance(), [$params]);
                            }
                        } else {
                            socket_write($client, 'Method does not exist');
                        }
                        //Return the result after running to the client
                        socket_write($client, $data);
                    }
                } else {
                    socket_write($client, 'Object or method does not exist');
                }

                // Close client
                socket_close($client);
            }
        } while (true);
    }

    /**
     * Destructor function.
     */
    public function __destruct()
    {
        socket_close($this->server);
    }
}
//This port is the best for Mac.
new rpcServer('127.0.0.1', 8080, '/service');

rpc client of simple pure PHP code

<?php

/**
 * rpc Client class.
 */
class rpcClient
{
    protected $client = null;
    protected $url_info = [];   // Remote call URL component

    public function __construct($url)
    {
        // Parsing URL
        $this->url_info = parse_url($url);
    }

    /**
     * Return data function.
     *
     * @param [type] $data
     *
     * @return mixed
     */
    public function response($data)
    {
        if (is_array($data)) {
            $data = json_encode($data);
        }
        echo $data.PHP_EOL;

        return;
    }

    public function __call($name, $arguments)
    {
        $socketHandler = fsockopen($this->url_info['host'], $this->url_info['port']);
        // Class name of the pass through call
        $class = basename($this->url_info['path']);
        // Pass the parameters of the call
        $args = '';
        if (isset($arguments[0])) {
            $args = json_encode($arguments[0]);
        }
        // Send our customized protocol data to the server
        $data = "Rpc-Class: {$class};".PHP_EOL
               ."Rpc-Method: {$name};".PHP_EOL
               ."Rpc-Params: {$args};".PHP_EOL;
        fputs($socketHandler, $data);
        $start_time = time();
        $responseData = '';
        while (!feof($socketHandler)) {
            $responseData .= fread($socketHandler, 1024);

            $diff = time() - $start_time;
            if ($diff > 24) {
                die('Timeout!n');
            }

            $status = stream_get_meta_data($socketHandler);
            if ($status['timed_out']) {
                $this->response('Stream Timeout!n');
            }
        }

        var_dump($responseData);
        fclose($socketHandler);
    }
}
//This port is the best for Mac.
$rpcClient = new RpcClient('http://127.0.0.1:8080/test');
echo $rpcClient->demo1(['title' => 'Request title', 'content' => 'Request content']);

test.php of the file in the service directory

<?php

class test
{
    public function __construct()
    {
    }

    public function demo1($data = '')
    {
        return 'This is test::demo1 Request parameters returned: '.$data;
    }

    public function demo2($data = '')
    {
        return 'This is test::demo2 Request parameters returned: '.$data;
    }
}

Keywords: Programming PHP socket Mac network

Added by im8kers on Thu, 24 Oct 2019 01:13:27 +0300