Sophie

Sophie

distrib > Mageia > 7 > armv7hl > media > core-release > by-pkgid > bee1fec4b7d6ab894248f735d3d4f6d1 > files > 29

python2-pysearpc-3.1.0-2.mga7.noarch.rpm

Introduction [![Build Status](https://secure.travis-ci.org/haiwen/libsearpc.png?branch=master)](http://travis-ci.org/haiwen/libsearpc)
============

Searpc is a simple C language RPC framework based on GObject system. Searpc
handles the serialization/deserialization part of RPC, the transport
part is left to users.

The serialization/deserialization uses JSON format via json-glib
library. A serialized json object is returned from server to client
after executing the RPC function. Each RPC function defined in the
server side should take an extra GError argument to report error. The
returned json object contains three fields:

* **ret**:          the return value of the RPC function
* **err_code**:     error code. This field is only set if the RPC function 
    reports an error.
* **err_msg**:      error message. This field is only set if the RPC function 
    reports an error.

Compile
=======

Just

    ./autogen.sh; ./configure; make; make install
    
To enable profile, Use
   
    CFLAGS="-DPROFILE" ./configure
    
When profile is enabled, the time spend in each rpc call will be printed.

Example
=======

Client
------

In the client side, you need to:

* Create a rpc_client and supply the transport function.
* write code to send the request to server and get the resonpse from it.


### Create rpc_client ###

The client needs to create a SearpcClient object and supply a
transport function. For example:


    /* create an rpc_client and supply the transport function. */
    SearpcClient *rpc_client;
    rpc_client = searpc_client_new();
    rpc_client->transport = transport_callback;
    rpc_client->arg = &sockfd;

Suppose we have a `get_substring` function defined in server as follows:

    gchar *get_substring (const gchar *orig_str, int sub_len, GError **error)
    
To call this function, we type:

    gchar* result;
    GError *error = NULL;
    result = searpc_client_call__string (client, "get_substring", &error,
                                         2, "string", "hello", "int", 2);

`string` in `searpc_client_call__string` specify the return type. "get_substring"
is the function name. The remain parameters specify the number of parameters the rpc
function took and the type of each parameter and its value. So

    2, "string", "hello", "int", 2
    
means "get_substring" takes 2 parameters, the first is of type "string", the value is
"hello", the second is of type "int", the value is 2.


### Transport function ###

When the client-side function is called, Searpc does the following work:
    
* Pack the function name and the params into JSON data format.
* Call your transport function to send the JSON data
    to the server, and get the returned data from the server.
* Unpack the returned JSON data and return the value as the return
    value of the client-side function.

Your transport function is supposed to:

* Send the request data to the server.
* Receive the returned data from the server.

The prototype of the transport function is:        

    /* 
     * arg: rpc_client->arg. Normally a socket number.
     * fcall_str: the JSON data stream generated by Searpc.
     * fcall_len: the length of `fcall_str`.
     * ret_len: place to get the length of the returned json data stream.
     * Returns: A newly allocated string stores the JSON data stream.
     */
    static char *transport_callback (void *arg, const char *fcall_str, size_t fcall_len, size_t *ret_len);

    
Server
------

In the server side, you need to:

* Init searpc server
* Create services and register your functions
* write code to receive the request and send the result

And Searpc handles the others for you.

### Concepts ###

* **Marshal**: The process of unpacking the function arguments from
    JSON data, call the RPC function and packing the result into JSON
    data format is called marshalling. The function used to
    pack the result is called a **marshal**.
 
* **Signature**: Every function has a signature determined by its
    return type and parameter types. Knowning a function's signature
    enable us to use a corresponding marshal to call it and convert
    the result into json string.


### Init Searpc Server ###

First write rpc_table.py to contain the rpc function signatures as follows:

    
    # [ <ret-type>, [<arg_types>] ]
    func_table = [
         [ "int", ["string"] ],
         [ "string", ["int", "string"] ],
    ]

Add makefile rule:

    searpc-signature.h searpc-marshal.h: rpc_table.py
        python searpc-codegen.py rpc_table.py
        
`searpc-signature.h` and `searpc-marshal.h` will be created containing the
function signatures and corresponding marshals. `searpc-marshal.h` also contains
a function called `register_marshals`.

Then we init the server as follows:


    #include "searpc-signature.h"
    #include "searpc-marshal.h"

    static void
    init_rpc_service(void)
    {
        /* register_marshals is defined in searpc-marshal.h */
        searpc_server_init(register_marshals);
    }
    

### Register Functions ###

To register a function we first need to create a service. A service is
a set of functions. 

Suppose we want to make `searpc_strlen` callable from some network
clients, we can do this by putting the following code somewhere:

    static int
    searpc_strlen(const char *str)
    {
        if (str == NULL)
            return -1;
        else
            return strlen(str);
    }
    
    static void
    register_functions()
    {
    
        searpc_create_service("searpc-demo");

        /* The first parameter is the implementation function.
         * The second parameter is the name of the rpc function the 
         * client would call.
         * The third parameter is the signature.
         */
        searpc_server_register_function("searpc-demo",
                                        searpc_strlen,
                                        "searpc_strlen",
                                        searpc_signature_int__string());
     }

    
The `seaprc_server_register_function` routine registers a function as
a RPC function. The
prototype of this function is:

    /*
     * service:     the name of the service
     * func:        pointer to the function you want to register
     * fname:       the name of the function. It would be the key of your 
     *              function in the fucntion hash table.
     * signature:   the identifier used to get the corresponding marshal.
     * Returns:     a gboolean value indicating success or failure
     */
     gboolean searpc_server_register_function (const char *service,
                                               void* func,
                                               const gchar *fname,
                                               gchar *signature);



### Call the RPC fucntion  ###

After the registration, you should listen to the socket and wait for the
incoming request data stream. Once you get a valid request, call the
`searpc_server_call_function()` routine, which will automatically do the
following work for you:

* Parse the JSON data stream to resolve the function name and the data.
* Lookup the function in internal function table according to the funcname.
* If a proper function is found, call the function with the given params.
* Packing the result into a JSON data string.
    
The prototype of `searpc_server_call_function` is:
    
    /*
     * service: Service name.
     * data:    The incoming JSON data stream.
     * len:     The length of **`data`**.
     * ret_len: Place to hold the length of the JSON data stream to be returned
     * Returns: The JSON data containing the result of the RPC
     */
    gchar* searpc_server_call_function (const char *service,
                                        gchar *data, gsize len, gsize *ret_len)

The value returned by `searpc_server_call_function()` is the JSON data
ready to send back to the client. 

Note, the JSON data stream from client does not contain the service
name, it's left to the transport layer to solve the problem. There are
several ways, for example:

1. You may listen on different sockets and determine the service by
   the incoming socket.
2. The client transport function prepend the service name into the request 
   before the json data, and the server transport function first read the service
   name and read the json data.

Pysearpc
========

**Pysearpc** is the Python binding of **Searpc**. Only the client side function is
supported. To use it, simply define a class which inherits **SearpcClient**, and
provide a `call_remote_func_sync` method, which is equivalent to the
`transport_callback`.

To define your RPC funtion, use the `@searpc_func` decorator. It is
equivalent to `SEARPC_CLIENT_DEFUN_XXX__YYY` macro. To define a RPC
function which accepts multiple params, here is an example:

    class SampeSearpcClient(SearpcClient):
        def call_remote_func_sync(self, fcall_str):
            # your transport code here
            ...
        
        @searpc_func("int", ["string", "string"])
        def searpc_demo_func(self):
            # this is enough for the client side
            pass

See the demo program for a more detailed example.


Demos
=====

There are well-commented demos in both C and Python. 

* **searpc-demo-server.c**: The server side demo program
* **searpc-demo-client.c**: The client side demo in C
* **pysearpc-demo-client.py**: The client side demo in Python

To run the demo, run the server demo in a shell, and run the client
demo in another. To run the python demo, you should first install the
package and setup the **PYTHONPATH** appropriately.


Dependency
==========

The following packages are required to build libsearpc:

*  glib-2.0      >=        2.26.0      
*  gobject-2.0   >=        2.26.0
*  jansson       >=        2.2.1
*  python simplejson (for pysearpc)