2014-06-09 07:02:41 +00:00
|
|
|
Introduction [](http://travis-ci.org/haiwen/libsearpc)
|
2011-05-18 03:06:01 +00:00
|
|
|
============
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
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
|
2012-06-23 14:45:25 +00:00
|
|
|
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:
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
* **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.
|
|
|
|
|
2011-12-25 07:30:15 +00:00
|
|
|
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.
|
2011-04-09 03:04:02 +00:00
|
|
|
|
2011-04-08 12:27:39 +00:00
|
|
|
Example
|
2011-05-18 09:24:34 +00:00
|
|
|
=======
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
Client
|
|
|
|
------
|
|
|
|
|
|
|
|
In the client side, you need to:
|
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
* Create a rpc_client and supply the transport function.
|
2011-04-08 12:27:39 +00:00
|
|
|
* write code to send the request to server and get the resonpse from it.
|
|
|
|
|
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
### Create rpc_client ###
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
The client needs to create a SearpcClient object and supply a
|
|
|
|
transport function. For example:
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
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.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
### 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.
|
2011-05-18 03:06:01 +00:00
|
|
|
* fcall_len: the length of `fcall_str`.
|
2011-04-08 12:27:39 +00:00
|
|
|
* ret_len: place to get the length of the returned json data stream.
|
2011-05-18 03:06:01 +00:00
|
|
|
* Returns: A newly allocated string stores the JSON data stream.
|
|
|
|
*/
|
2011-04-08 12:27:39 +00:00
|
|
|
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:
|
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
* Init searpc server
|
|
|
|
* Create services and register your functions
|
2011-04-08 12:27:39 +00:00
|
|
|
* write code to receive the request and send the result
|
|
|
|
|
|
|
|
And Searpc handles the others for you.
|
|
|
|
|
|
|
|
### Concepts ###
|
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
* **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**.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
* **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.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
### Init Searpc Server ###
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
First write rpc_table.py to contain the rpc function signatures as follows:
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
|
|
|
|
# [ <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);
|
|
|
|
}
|
|
|
|
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
### 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());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
The `seaprc_server_register_function` routine registers a function as
|
2012-06-23 14:45:25 +00:00
|
|
|
a RPC function. The
|
2011-04-08 12:27:39 +00:00
|
|
|
prototype of this function is:
|
|
|
|
|
|
|
|
/*
|
2012-06-23 14:45:25 +00:00
|
|
|
* service: the name of the service
|
2011-04-08 12:27:39 +00:00
|
|
|
* 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
|
2011-05-18 09:24:34 +00:00
|
|
|
*/
|
2012-06-23 14:45:25 +00:00
|
|
|
gboolean searpc_server_register_function (const char *service,
|
|
|
|
void* func,
|
|
|
|
const gchar *fname,
|
|
|
|
gchar *signature);
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 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
|
2011-05-18 03:06:01 +00:00
|
|
|
`searpc_server_call_function()` routine, which will automatically do the
|
2011-04-08 12:27:39 +00:00
|
|
|
following work for you:
|
|
|
|
|
|
|
|
* Parse the JSON data stream to resolve the function name and the data.
|
2012-06-23 14:45:25 +00:00
|
|
|
* Lookup the function in internal function table according to the funcname.
|
2011-04-08 12:27:39 +00:00
|
|
|
* If a proper function is found, call the function with the given params.
|
|
|
|
* Packing the result into a JSON data string.
|
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
The prototype of `searpc_server_call_function` is:
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
/*
|
2012-06-23 14:45:25 +00:00
|
|
|
* service: Service name.
|
2011-04-08 12:27:39 +00:00
|
|
|
* 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
|
2011-05-18 09:24:34 +00:00
|
|
|
*/
|
2012-06-23 14:45:25 +00:00
|
|
|
gchar* searpc_server_call_function (const char *service,
|
|
|
|
gchar *data, gsize len, gsize *ret_len)
|
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
The value returned by `searpc_server_call_function()` is the JSON data
|
|
|
|
ready to send back to the client.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2012-06-23 14:45:25 +00:00
|
|
|
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.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
Pysearpc
|
2011-05-18 03:06:01 +00:00
|
|
|
========
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
**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
|
2011-05-18 03:06:01 +00:00
|
|
|
provide a `call_remote_func_sync` method, which is equivalent to the
|
|
|
|
`transport_callback`.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
To define your RPC funtion, use the `@searpc_func` decorator. It is
|
|
|
|
equivalent to `SEARPC_CLIENT_DEFUN_XXX__YYY` macro. To define a RPC
|
2011-04-08 12:27:39 +00:00
|
|
|
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
|
2011-05-18 09:24:34 +00:00
|
|
|
=====
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-05-18 03:06:01 +00:00
|
|
|
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.
|
2011-04-08 12:27:39 +00:00
|
|
|
|
|
|
|
|
2011-04-09 03:04:02 +00:00
|
|
|
Dependency
|
2011-05-18 03:06:01 +00:00
|
|
|
==========
|
2011-04-09 03:04:02 +00:00
|
|
|
|
|
|
|
The following packages are required to build libsearpc:
|
|
|
|
|
2013-08-09 04:56:37 +00:00
|
|
|
* glib-2.0 >= 2.26.0
|
|
|
|
* gobject-2.0 >= 2.26.0
|
|
|
|
* jansson >= 2.2.1
|
2012-06-03 16:19:16 +00:00
|
|
|
* python simplejson (for pysearpc)
|