2021SC@SDUSC-山大智云源码分析(8)
2021/11/27 22:12:05
本文主要是介绍2021SC@SDUSC-山大智云源码分析(8),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Implementatin of a searpc transport based on named pipe
前言
经过之前的分析,已经分析完成了searpc服务器、客户端以及二者连接的基本原理。接下来对searpc-name-pipe-transport
文件中的基于命名管道的searpc传输的应用进行分析。
named-pipe
管道是一个有两端的对象。一个进程向管道写入信息,而另一个进程从管道读取信息。其本质是用于进程间通信的共享内存区域,确切的说是线程间通信方法。
命名管道是全双工的,且支持网络通信。创建管道的进程称为管道服务器,连接到这个管道的进程成为管道客户端。命名管道支持多客户端连接,支持双向通信。
searpc-named-pipe-transport.h
首先定义searpc命名管道服务器端的接口,及其构造方法
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_thread
用于创建一个监听可能的连接的线程 -
char path[4096]
与SearpcNamedPipe pipe_fd
用于存放管道服务器的路径与管道号 -
GThreadPool *named_pipe_server_thread_pool
是一个线程池,其作用是调用rpc函数
然后
定义searpc命名管道客户端的接口,及其构造方法
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);
其成员属性同上
searpc-named-pipe-transport-c
创建SearpcClient
创建结构体,用于在客户端向管道中传输数据
typedef struct { SearpcNamedPipeClient* client; char *service; } ClientTransportData;
其属性包含searpc命名管道客户端SearpcNamedPipeClient
与服务名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; }
上述两个方法用于创建一个searpc-client,但是其原理并非是与searpc-demo-client.c
中相同(基于socket
),而是基于named-pipe。
在此回顾SearpcClient
的定义
struct _SearpcClient { TransportCB send; void *arg; AsyncTransportSend async_send; void *async_arg; };
其中void *arg
属性在之前的实现中为传输用socket
的sockfd,因此我们得知,其类型之所以为void *是为了应对基于不同传输方式的实现。
在searpc_client_with_named_pipe_transport
方法中,指定了SearpcClient
的传输函数与管道客户端(等价于sockfd);在searpc_create_named_pipe_client
,方法中创建了SearpcNamedPipeClient
,并指定了地址。
传输函数
然后讨论传输函数searpc_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; }
我们已经知道了传输函数的作用是,将请求数据发送到服务端,并从服务端接收返回数据。其参数列表及各参数作用与searpc-client-demo
中相同。
首先将传入的参数void *arg
转化为ClientTransportData
,并获取到NamedPipeClient
;然后根据data->service
、fcall_str
、fcall_len
创建json字符串
在这里让我疑惑的是,之前在searpc-demo-client
中遇到的传输函数,并未指定service
名。查询传输函数的声明,也和被调用rpc函数的服务名无关
typedef char *(*TransportCB)(void *arg, const gchar *fcall_str, size_t fcall_len, size_t *ret_len);
而在服务器调用rpc函数的方法中,需要传入服务名,并通过它来查找rpc函数
searpc_server_call_function (const char *svc_name, gchar *func, gsize len, gsize *ret_len)
经过查看searpc-demo-server
中的实现,发现是在调用时由程序员指定的服务名。
在此之后调用pipe_write_n
想管道客户端的pipe_fd
写入上述json字符串;然后调用pipe_read_n
从管道读入返回值,并写入到内存,最后设置返回值长度并返回。
request_to_json
本方法的作用是将服务名、被调用rpc函数的参数封装进json字符串中
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
向json对象中设置键值对
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
与earpc-demo-server
中的writen
与readn
方法相同
这篇关于2021SC@SDUSC-山大智云源码分析(8)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27文件掩码什么意思?-icode9专业技术文章分享
- 2024-12-27如何使用循环来处理多个订单的退款请求,代码怎么写?-icode9专业技术文章分享
- 2024-12-27VSCode 在编辑时切换到另一个文件后再切回来如何保持在原来的位置?-icode9专业技术文章分享
- 2024-12-27Sealos Devbox 基础教程:使用 Cursor 从零开发一个 One API 替代品 审核中
- 2024-12-27TypeScript面试真题解析与实战指南
- 2024-12-27TypeScript大厂面试真题详解与解析
- 2024-12-26怎么使用nsenter命令进入容器?-icode9专业技术文章分享
- 2024-12-26导入文件提示存在乱码,请确定使用的是UTF-8编码怎么解决?-icode9专业技术文章分享
- 2024-12-26csv文件怎么设置编码?-icode9专业技术文章分享
- 2024-12-25TypeScript基础知识详解