栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

2021SC@SDUSC-山大智云源码分析(2)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021SC@SDUSC-山大智云源码分析(2)

2021SC@SDUSC

目录

前言-libsearpc项目介绍及结构

项目结构

demo分析

searpc-demo-seaver.c

项目中重要概念

初始化searpc服务器

创建服务并注册函数

接受并处理请求

监听socket

其他


前言-libsearpc项目介绍及结构

根据文档以及个人研究,seafile的所有与数据库相关操作、文件系统操作都在seafile-server项目中,而seahub则是一个django服务器,也就是说,seafile有两个服务器,那么这两个服务器必须进行数据交换,整个服务才能正常进行。

那么承载着两个服务器之间的中间件就是seaprc、这是一个rpc框架,主要的工作是接受网络传输的数据,然后再两个端进行数据的格式化和解析,但是传输过程需要由用户处理。

应当注意的是,searpc本身并不能创建服务器和客户端,需要手动使用socket等库建立端口监听,客户端需要自行向服务端建立连接。

项目结构

searpc是基于GObject系统的C语言rpc框架

  • demo:searpc的测试demo 

    • 包含C语言实现的服务端与客户端,以及python实现的客户端

  • lib:存放searpc的具体实现代码

  • pysearpc:对searpc的python语言的封装,仅支持客户端函数

demo分析

searpc-demo-seaver.c

根据文档得知,在服务器端,需要进行的操作如下:

  • 初始化searpc服务器

  • 创建服务并注册函数

  • 建立接受请求部分的相关代码

项目中重要概念
  • marshall:将函数参数从json形式中解包,调用rpc函数并且将返回结果封装为json数据的过程成为marshalling。将返回结果序列化为json的函数成为Marshall

  • signature:每个函数都有一个根据其返回类型以及参数列表定义的signature。知道一个函数的signature能够是我们使用其对应的Marshall来调用它并将其结果序列化

初始化searpc服务器

首先定义希望被调用的rpc函数

static int
searpc_strlen(const char *str)
{
    if (str == NULL)
        return -1;
    else
        return strlen(str);
}
​
static GList *
searpc_objlisttest(int count, int len, const char *str)
{
    GList *ret = NULL;
    int i;
    for (i = 0; i != count; ++i)
    {
        TestObject *obj = g_object_new(TEST_OBJECT_TYPE, NULL);
        obj->len = len;
        g_free(obj->str);
        obj->str = g_strdup(str);
        if (len == strlen(str))
            obj->equal = TRUE;
        ret = g_list_prepend(ret, obj);
    }
    return ret;
}

其次,在rpc_table.py文件中定义了rpc函数的signature,其形式如下:

# [ , [] ]
func_table = [
     [ "int", ["string"] ],
     [ "string", ["int", "string"] ],
]

通过makefile文件

searpc-signature.h searpc-marshal.h: rpc_table.py
    python searpc-codegen.py rpc_table.py

从而得到包含上述定义的函数signature以及对应的Marshall的文件:searpc-signature.h与 searpc-marshal.h

  • 其中searpc-marshal.h也包含一个register_marshals函数

目前我们准备好了需要被调用的rpc函数及其signature与marshall,然后就可以初始化searpc服务器

#include "searpc-signature.h"
#include "searpc-marshal.h"
​
static void
init_rpc_service(void)
{
    
    searpc_server_init(register_marshals);
}

创建服务并注册函数

为了能够通过rpc调用到函数,我们需要将创建好的函数定义在服务service中

  • service服务即为一组函数的集合

在init_rpc_service定义service并注册函数

static void
start_rpc_service(void)
{
    searpc_server_init(register_marshals);
    searpc_create_service("searpc-demo");
​
    
    searpc_server_register_function("searpc-demo",
                                    searpc_strlen,
                                    "searpc_strlen",
                                    searpc_signature_int__string());
    searpc_server_register_function("searpc-demo",
                                    searpc_objlisttest,
                                    "searpc_objlisttest",
                                    searpc_signature_objlist__int_int_string());
}

通过searpc_server_register_function方法,将一个函数注册为rpc函数

 gboolean searpc_server_register_function (const char *service,
                                           void* func,
                                           const gchar *fname,
                                           gchar *signature);
  • service:被注册函数所在服务名

  • func:希望注册的函数的指针,即函数的标识符

  • fname:函数名

  • signature:用于获得对应Marshall的标识

接受并处理请求

服务器始终监听端口等待请求,当获取到一个有效请求后,服务器调用searpc_server_call_function(),从而能够实现以下过程:

  • 解码json数据

  • 根据函数名寻找到所需函数

  • 根据参数列表调用函数

  • 将结果序列化

gchar* searpc_server_call_function (const char *service,
                                    gchar *data, gsize len, gsize *ret_len)
  • service:服务名

  • data:接收到json数据流

  • len:data的长度

  • ret_len:将要返回的数据流长度的指针

但是从客户端获取到了json数据流并不包含服务名,因此需要从传输层解决这个问题

  • 可以根据监听不同的sockets判断是哪个服务

  • 也可以由客户端在传输时,将服务名插入到json数据之前,因此服务端可以先获取到服务名,再得到json数据

监听socket

在初始化searpc服务器后,创建一个socket;然后,将这个socket绑定到内存中并开始监听

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        fprintf(stderr, "socket failed: %sn", strerror(errno));
        exit(-1);
    }
​
    int on = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
    {
        fprintf(stderr, "setsockopt of SO_REUSEADDR error: %sn", strerror(errno));
        exit(-1);
    }
​
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(12345);
​
    ret = bind(listenfd, (struct sockaddr *)&server_addr,
               sizeof(server_addr));
​
    ret = listen(listenfd, 5);

当获取到一个正确的请求时,将发送的json数据流读入到pac,然后调用searpc_server_call_function,完成对请求的处理,并得到调用结果;然后同样使用socket将结果发送到客户端

        GError *error = NULL;
​
        clilen = sizeof(client_addr);
        connfd = accept(listenfd, (struct sockaddr *)&client_addr, &clilen);
        if (connfd < 0)
        {
            fprintf(stderr, "accept failed: %sn", strerror(errno));
            continue;
        }
​
        
        pac = read_packet(connfd, buf);
        if (pac == NULL)
        {
            fprintf(stderr, "read packet failed: %sn", strerror(errno));
            exit(-1);
        }
​
        gsize ret_len;
        int fcall_len = ntohs(pac->length);
        
        char *res = searpc_server_call_function("searpc-demo", pac->data, fcall_len,
                                                &ret_len);
        pac_ret = (packet *)buf;
        pac_ret->length = htons((uint16_t)ret_len);
        memcpy(pac_ret->data, res, ret_len);
​
        
        if (writen(connfd, buf, PACKET_HEADER_LENGTH + ret_len) == -1)
        {
            fprintf(stderr, "write packet failed: %sn", strerror(errno));
            exit(-1);
        }
    }

其他

由于序列化过程使用的是json形式,根据头文件的引用可以推断出,在这个过程中使用了json-glib库

#include 
#include 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/329309.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号