2021SC@SDUSC -Shan Dazhiyun Source Code Analysis

Implementatin of a searpc transport based on named pipe

Preface

After previous analysis, the basic principles of searpc server, client and their connection have been analyzed. Next, the application of named pipe-based searpc transport in searpc-name-pipe-transport file is analyzed.

named-pipe

A pipe is an object with two ends. One process writes information to the pipeline, while another process reads information from the pipeline. It is essentially a shared memory area used for inter-process communication, specifically the method of inter-thread communication.

Named pipes are full duplex and support network communication. The process of creating a pipeline is called a pipeline server, and the process connecting to the pipeline becomes a pipeline client. Named pipes support multi-client connections and two-way communication.

searpc-named-pipe-transport.h

First, define the server-side interface of the searpc named pipe and how to construct it

struct _SearpcNamedPipeServer {
    char path[4096];
    pthread_t listener_thread;
    SearpcNamedPipe pipe_fd;
    GThreadPool *named_pipe_server_thread_pool;
};
​
typedef struct _SearpcNamedPipeServer LIBSEARPC_API SearpcNamedPipeServer;
​
LIBSEARPC_API
SearpcNamedPipeServer* searpc_create_named_pipe_server(const char *path);
​
LIBSEARPC_API
SearpcNamedPipeServer* searpc_create_named_pipe_server_with_threadpool(const char *path, int named_pipe_server_thread_pool_size);
​
LIBSEARPC_API
int searpc_named_pipe_server_start(SearpcNamedPipeServer *server);
  • Pthread_ T listener_ Threads are used to create a thread that listens for possible connections

  • char path[4096] and SearpcNamedPipe pipe_fd is used to store the path and piping number of the piping server

  • GThreadPool *named_ Pipe_ Server_ Thread_ A pool is a thread pool whose function is to call the rpc function

Then?

Define the searpc named pipe client interface and how to construct it

struct _SearpcNamedPipeClient {
    char path[4096];
    SearpcNamedPipe pipe_fd;
};
​
typedef struct _SearpcNamedPipeClient LIBSEARPC_API SearpcNamedPipeClient;
​
LIBSEARPC_API
SearpcNamedPipeClient* searpc_create_named_pipe_client(const char *path);
​
LIBSEARPC_API
SearpcClient * searpc_client_with_named_pipe_transport(SearpcNamedPipeClient *client, const char *service);
​
LIBSEARPC_API
int searpc_named_pipe_client_connect(SearpcNamedPipeClient *client);
​
LIBSEARPC_API
void searpc_free_client_with_pipe_transport (SearpcClient *client);

Its member properties are the same as above

searpc-named-pipe-transport-c

Create SearpcClient

Create a structure for transferring data from client to pipeline

typedef struct {
    SearpcNamedPipeClient* client;
    char *service;
} ClientTransportData;
 

Its properties include the searpc named pipe client SearpcNamedPipeClient and the service name service

SearpcClient*
searpc_client_with_named_pipe_transport(SearpcNamedPipeClient *pipe_client,
                                        const char *service)
{
    SearpcClient *client= searpc_client_new();
    client->send = searpc_named_pipe_send;
​
    ClientTransportData *data = g_malloc(sizeof(ClientTransportData));
    data->client = pipe_client;
    data->service = g_strdup(service);
​
    client->arg = data;
    return client;
}
​
SearpcNamedPipeClient* searpc_create_named_pipe_client(const char *path)
{
    SearpcNamedPipeClient *client = g_malloc0(sizeof(SearpcNamedPipeClient));
    memcpy(client->path, path, strlen(path) + 1);
    return client;
}

These two methods are used to create a searpc-client, but they do not work the same way (socket-based) as in searpc-demo-client.c, but rather on named-pipe.

Review the definition of SearpcClient here

struct _SearpcClient {
    TransportCB send;
    void *arg;
    
    AsyncTransportSend async_send;
    void *async_arg;
};

The void *arg attribute is the sockfd of the socket used for transport in the previous implementation, so we know that its type is void * to accommodate implementations based on different transport modes.

In searpc_ Client_ With_ Named_ Pipe_ In the transport method, the transport function and the management client (equivalent to sockfd) of the SearpcClient are specified. In searpc_create_named_pipe_client, which creates the SearpcNamedPipeClient in the method and specifies the address.

Transfer function

Then the transfer function searpc_is discussed Named_ Pipe_ Send.

char *searpc_named_pipe_send(void *arg, const gchar *fcall_str,
                             size_t fcall_len, size_t *ret_len)
{
    /* g_debug ("searpc_named_pipe_send is called\n"); */
    ClientTransportData *data = arg;
    SearpcNamedPipeClient *client = data->client;
​
    char *json_str = request_to_json(data->service, fcall_str, fcall_len);
    guint32 len = (guint32)strlen(json_str);
​
    if (pipe_write_n(client->pipe_fd, &len, sizeof(guint32)) < 0) {
        g_warning("failed to send rpc call: %s\n", strerror(errno));
        free (json_str);
        return NULL;
    }
​
    if (pipe_write_n(client->pipe_fd, json_str, len) < 0) {
        g_warning("failed to send rpc call: %s\n", strerror(errno));
        free (json_str);
        return NULL;
    }
​
    free (json_str);
​
    if (pipe_read_n(client->pipe_fd, &len, sizeof(guint32)) < 0) {
        g_warning("failed to read rpc response: %s\n", strerror(errno));
        return NULL;
    }
​
    char *buf = g_malloc(len);
​
    if (pipe_read_n(client->pipe_fd, buf, len) < 0) {
        g_warning("failed to read rpc response: %s\n", strerror(errno));
        g_free (buf);
        return NULL;
    }
​
    *ret_len = len;
    return buf;
}

We already know that the transport function sends request data to the server and receives the return data from the server. Its parameter list and function are the same as those in searpc-client-demo.

First convert the incoming parameter void *arg to ClientTransportData and get NamedPipeClient; Then according to data->service, fcall_str, fcall_len creates json string

What confuses me here is that the transport function I previously encountered in searpc-demo-client does not specify a service name. Query the declaration of the transfer function, also independent of the service name of the invoked rpc function

typedef char *(*TransportCB)(void *arg, const gchar *fcall_str,
                             size_t fcall_len, size_t *ret_len);

In the way the server calls the rpc function, you need to pass in the service name and use it to find the rpc function

searpc_server_call_function (const char *svc_name,
                             gchar *func, gsize len, gsize *ret_len)

After looking at the implementation in searpc-demo-server, it is found that the name of the service was specified by the programmer at the time of the call.

Call pipe_after Write_ NPipe_to Pipe Client FD writes the above json string; Then call pipe_read_n Reads the return value from the pipeline, writes it to memory, and finally sets the length of the return value and returns it.

request_to_json

The purpose of this method is to encapsulate the service name, the parameters of the called rpc function into a json string

request_to_json (const char *service, const char *fcall_str, size_t fcall_len)
{
    json_t *object = json_object ();
​
    char *temp_request = g_malloc0(fcall_len + 1);
    memcpy(temp_request, fcall_str, fcall_len);
​
    json_object_set_string_member (object, "service", service);
    json_object_set_string_member (object, "request", temp_request);
​
    g_free (temp_request);
​
    char *str = json_dumps (object, 0);
    json_decref (object);
    return str;
}

json_object_set_string_member

Setting key-value pairs to json objects

static void json_object_set_string_member (json_t *object, const char *key, const char *value)
{
    json_object_set_new (object, key, json_string (value));
}

pipe_write_n&pipe_read_n

The same writen and read methods as in earpc-demo-server

Tags: Python C

Posted on Tue, 30 Nov 2021 21:28:26 -0500 by mpb001