commit a4017c7c3e2ab3cb67ff31755c8d282ad8a24bd1 Author: lins05 Date: Fri Apr 8 20:27:39 2011 +0800 first commit diff --git a/README b/README new file mode 100755 index 0000000..e2705f4 --- /dev/null +++ b/README @@ -0,0 +1,212 @@ + +Introduction +====== + +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 JsonObject 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 JsonObject +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. + +Example +====== + +Client +------ + +In the client side, you need to: + +* Define your RPC function with the our marco. +* write code to send the request to server and get the resonpse from it. + + +### Define your RPC function ### + +The client define RPC functions using macros defined in +**`searpc-client.h`**. To call the functions, the client needs to create +a SearpcClient object and supply a transport function. For example: + + /* + * define a RPC function which takes a string argument and returns + * an integer. The xxx_int__string naming convention is used to + * indicate the types of the param(string) and the return value(int). + */ + SEARPC_CLIENT_DEFUN_INT__STRING(searpc_demo_int__string) + + /* 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; + +The **`SEARPC_CLIENT_DEFUN_XXX__YYY`** macro defines a client side RPC function. +**XXX** stands for the return type and **YYY** stands for the param type. If there +are multiple params, just append every param type there. For example, +**`SEARPC_CLIENT_DEFUN_INT__STRING_STRING`** defines a RPC function which takes two +string params and returns an integer value. + + +### 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: the JSON data stream ready for transporting. + */ + 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: + +* Register your function +* write code to receive the request and send the result + +And Searpc handles the others for you. + +### Concepts ### + +* **`Marshal`**: The process of packing the result of the RPC function + into JSON data format stream is called marshalling, and the + function used to pack the result is called a **`marshal`**. + +* **`Signature`**: Signatures are used to identify different types of + marshals. After the execution of a RPC function on the server side, + different types of results need different marshals. + +* **`Function table`**: The hash table used to store the (funcname, func) + pairs. + +* **`Marshal table`**: The hash table used to store the (signature, + marshal) pairs. + + +### Register your function ### + +The **`seaprc_server_register_function`** routine registers a function as +a RPC function. It inserts your function into the function table. The +prototype of this function is: + + /* + * 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 (void *func, const gchar *fname, const gchar *signature) + + Before calling **`searpc_server_register_function`**, you need to call +**`searpc_server_init()`** to do the initialization work. + + +### 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 the 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: + + /* + * 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 (gchar *data, gsize len, gsize *ret_len) + +The value returned by **`searpc_server_call_function()`** is the JSON data +ready for transportation, which you can send to the client directly. + +In the client side, Searpc would unpack the data stream and return the value +contained within it as the return value of the client-side RPC function defined +in the macro **`SEARPC_CLIENT_DEFUN_XXX__YYY()`**. + + +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(either +the C or Python demo) in another. + + +Thread Safety +====== + + +Extend Searpc +====== + +To support new rpc function types, + +1. edit **`rpc_table.py`**, +2. call **make genrpc**. +3. If the return type is not supported yet, you need also define the + corresponding **`searpc_fret_*`** function.