Linux应用开发实验班——JSON-RPC

目录

前言

1.是什么JSON-RPC

2.常用的JSON函数

1.创建JSON

2.根据名字获取JSON

3.获取JSON的值

4.删除JSON

3.如何进行远程调用

服务器

客户端

4.基于JSON-RPC进行硬件操作

课程链接


前言

学习的课程是百问网韦东山老师的课程,对更详细步骤感兴趣的同学,可以去学习视频课程。代码里的led和dht11的驱动都是学习韦老师的课程写的。

1.是什么JSON-RPC

JSON(JavaScript Object Notation,JavaScript 对象表示法)是基于 ECMAScript的

一个子集设计的,是一种开放标准的文件格式和数据交换格式,它易于人阅读和编写,同

时也易于机器解析和生成。JSON独立于语言设计,很多编程语言都支持JSON格式的数据交

换。JSON 是一种常用的数据格式,在电子数据交换中有多种用途,包括与服务器之间的

Web 应用程序的数据交换。其简洁和清晰的层次结构有效地提升了网络传输效率,使其成

为理想的数据交换语言。其文件通常使用扩展名.json。

1.它的关键成员是**"name:value"**,value有多种形式。

{ "name": "John", "age": 30, "isStudent": false , "ptr": null }

2.上述例子中,有3种取值:"John"是字符串,30是整数,false是bool类型,null是 空值。 值的类型也可以是数组,比如:

{ "fruits": ["apple","banana", "cherry"] }

3.值的类型,还可以是一个JSON对象,比如:

{

"title":"JSON Example",

"author": { "name":"John Doe", "age": 35, "isVerified":true }, "tags":["json", "syntax", "example"],

"rating": 4.5, "isPublished":false,

"comments": null

}

2.常用的JSON函数

1.创建JSON

使用字符串创建一个cJSON结构体:

cpp 复制代码
cJSON *json = cJSON_Parse(const char *value);
参数:
const char *value:这是唯一的参数,是一个指向要解析的 JSON 字符串的指针。
该字符串需要以 null 结尾。
返回值:
如果解析成功,函数会返回一个指向 cJSON 结构体的指针。
这个结构体表示了解析后的 JSON 数据,可以通过其他 cJSON 库提供的函数
(如 cJSON_GetObjectItem 等)来访问和操作其中的具体元素。
如果解析失败,函数会返回 NULL。导致解析失败的原因可能是 JSON 字符串的格式不正确、
存在非法字符或其他不符合 JSON 规范的情况。
注意事项:使用 cJSON_Parse 函数时,在调用之后一定要检查返回值是否为 NULL,
以确保解析成功。并且在使用完解析后的 cJSON 结构体后,
需要调用 cJSON_Delete 函数来释放内存,以避免内存泄漏。

2.根据名字获取JSON

cpp 复制代码
cJSON *value = cJSON_GetObjectItem(const cJSON * const object, const char * const string);
参数:
const cJSON * const object:这是一个指向 cJSON结构体的常量指针,
代表待查找的 JSON 对象。该参数指定了在哪个 JSON 对象中进行成员查找。
const char * const string:这是一个指向字符串的常量指针,
代表要查找的成员的名称。该名称应与 JSON 对象中成员的键相对应。
返回值:
如果在给定的 JSON对象中找到了指定名称的成员,则返回一个指向该成员的 cJSON结构体的指针。
通过这个返回的指针,可以进一步访问该成员的具体信息,如值的类型、字符串值、数值等。
如果没有找到指定名称的成员,则返回 NULL。所以在使用该函数后,
通常需要检查返回值是否为 NULL,以确保能够正确地处理查找结果。

3.获取JSON的值

cJSON 结构体里存有值:valuestring、valueint、valuedouble,直接使用即可:

示例:

4.删除JSON

cpp 复制代码
cJSON_public(void) cJSON_Delete(cJSON *item);
参数:
cJSON *item:这是一个指向要释放的 cJSON 结构体的指针。
该指针指向的 cJSON 结构体可以是由 cJSON_Parse 函数解析 JSON 字符串后得到的根节点,
也可以是通过其他 cJSON 函数获取的子节点。
返回值:该函数没有明确的特定返回值类型定义,其主要作用是释放内存。
在调用该函数后,传入的 cJSON 结构体指针 item 及其所有相关的子结构体所占用的内存都会被释放。

3.如何进行远程调用

服务器

服务器的编写json-rpc为我们提供了初始化的API接口,直接调用就可以进行初始化。

1.初始化

cpp 复制代码
函数原型:
int jrpc_server_init(struct jrpc_server *server, int port_number)。
参数:
struct jrpc_server *server:这是一个指向jrpc_server结构体的指针,
用于传递服务器的配置信息和状态。通过这个参数,函数可以对服务器进行初始化设置。
int port_number:指定服务器要监听的端口号。
返回值:
返回一个整数。具体的返回值含义可能取决于函数的具体实现逻辑。
通常情况下,可能返回一些状态码,比如成功初始化时返回 0,
出现错误时返回非零值以表示不同类型的错误情况。

2.在服务器上注册新的过程

cpp 复制代码
函数原型:
int jrpc_register_procedure(struct jrpc_server *server, jrpc_function function_pointer, char *name, void *data)。
参数:
struct jrpc_server *server:指向 JSON-RPC 服务器结构体的指针,
用于在该服务器上注册新的过程。
jrpc_function function_pointer:一个函数指针,指向要注册的过程所对应的函数。
char *name:要注册的过程的名称。
void *data:一个通用指针,可以传递与注册的过程相关的额外数据。
返回值:
返回一个整数。如果注册成功,返回 0;如果出现错误,
比如内存分配失败或字符串复制失败,返回 -1。

3.启动 JSON-RPC 服务器

cpp 复制代码
函数原型:
void jrpc_server_run(struct jrpc_server *server)。
参数:
struct jrpc_server *server:指向 JSON-RPC 服务器结构体的指针。
这个结构体中包含了与服务器运行相关的信息,特别是其中的事件循环(通过server->loop访问)。
返回值:
无返回值(void)。

服务器会监听设置的端口,根据客户端发送来的消息选择要执行的过程,将数据以cjson结构体返回

4.在服务器不再使用时进行清理和资源释放操作

cpp 复制代码
函数原型:
void jrpc_server_destroy(struct jrpc_server *server)。
参数:
struct jrpc_server *server:指向 JSON-RPC 服务器结构体的指针。
这个结构体包含了服务器运行时的各种状态和资源信息。
返回值:
无返回值(void)。

客户端

客户端的编写,初始化和之前网络编程中的客户端编写是相同的。

初始化

这里我就直接放代码了,不懂可以查看直接网络编程那一篇的文章

cpp 复制代码
int RPC_Client_Init(void) 
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    int iRet;

    iSocketClient = socket(AF_INET, SOCK_STREAM, 0);

    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(PORT);  /* host to net, short */
    //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("127.0.0.1", &tSocketServerAddr.sin_addr);
    memset(tSocketServerAddr.sin_zero, 0, 8);


    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
    if (-1 == iRet)
    {
        printf("connect error!\n");
        return -1;
    }

    return iSocketClient;    
}

客户端选择要远程调用的过程

1.将消息构造成json类型的字符串,发送给给服务器

cpp 复制代码
 sprintf(buf, "{\"method\": \"add\", \"params\": [%d,%d], \"id\": \"2\" }", a, b);
 iLen = send(iSocketClient, buf, strlen(buf), 0);

2.服务器会返回一个cjson结构体的结果,解析cjson结构体获取结果

cpp 复制代码
iLen = read(iSocketClient, buf, sizeof(buf));
cJSON *root = cJSON_Parse(buf);
cJSON *result = cJSON_GetObjectItem(root, "result");
*sum = result->valueint;
cJSON_Delete(root);

4.基于JSON-RPC进行硬件操作

服务器端:

cpp 复制代码
#include <jsonrpc-c.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "rpc.h"
#include "led.h"
#include "dht11.h"

static struct jrpc_server my_server;

/* 参数: {"params" : [0|1]} */
cJSON * server_led_control(jrpc_context * ctx, cJSON * params, cJSON *id) {
    cJSON * status = cJSON_GetArrayItem(params,0);
    led_control(status->valueint);	
    return cJSON_CreateNumber(0);
}

/* 参数: {"params" : null} */
cJSON * server_dht11_read(jrpc_context * ctx, cJSON * params, cJSON *id) {
    int array[2];

    array[0] = array[1] = 0;

    while (0 != dht11_read((char *)&array[0], (char *)&array[1]));

    return cJSON_CreateIntArray(array, 2);
}

int RPC_Server_Init(void) 
{
    int err;
    
    err = jrpc_server_init(&my_server, PORT);
    if (err)
    {
        printf("jrpc_server_init err : %d\n", err);
    }
    
    jrpc_register_procedure(&my_server, server_led_control, "led_control", NULL );
    jrpc_register_procedure(&my_server, server_dht11_read, "dht11_read", NULL );

    jrpc_server_run(&my_server);
    jrpc_server_destroy(&my_server);

    return 0;
}

int main(int argc, char **argv)
{
    led_init();
    dht11_init();
    RPC_Server_Init();   
    return 0;
}

客户端:

cpp 复制代码
#include <jsonrpc-c.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "rpc.h"


int rpc_led_control(int iSocketClient, int on)
{
    char buf[100];
    int iLen;
    int ret = -1;

    sprintf(buf, "{\"method\": \"led_control\", \"params\": [%d], \"id\": \"2\" }", on);
    iLen = send(iSocketClient, buf, strlen(buf), 0);
    if (iLen ==  strlen(buf))
    {
        while (1) 
        {
            iLen = read(iSocketClient, buf, sizeof(buf));
            buf[iLen] = 0;
            if (iLen == 1 && (buf[0] == '\r' || buf[0] == '\n'))
                continue;
            else
                break;
        } 
        
        if (iLen > 0)
        {
            cJSON *root = cJSON_Parse(buf);
            cJSON *result = cJSON_GetObjectItem(root, "result");
            ret = result->valueint;
            cJSON_Delete(root);
            return ret;
        }
        else
        {
            printf("read rpc reply err : %d\n", iLen);
            return -1;
        }
    }
    else
    {
        printf("send rpc request err : %d, %s\n", iLen, strerror(errno));
        return -1;
    }
}

int rpc_dht11_read(int iSocketClient, char *humi, char *temp)
{
    char buf[300];
    int iLen;

    sprintf(buf, "{\"method\": \"dht11_read\"," \
                   "\"params\": [0], \"id\": \"2\" }");        
            
    iLen = send(iSocketClient, buf, strlen(buf), 0);
    if (iLen ==  strlen(buf))
    {
        while (1) 
        {
            iLen = read(iSocketClient, buf, sizeof(buf));
            buf[iLen] = 0;
            if (iLen == 1 && (buf[0] == '\r' || buf[0] == '\n'))
                continue;
            else
                break;
        } 
        
        if (iLen > 0)
        {
            cJSON *root = cJSON_Parse(buf);
            cJSON *result = cJSON_GetObjectItem(root, "result");
            if (result)
            {
                cJSON * a = cJSON_GetArrayItem(result,0);
                cJSON * b = cJSON_GetArrayItem(result,1);

                *humi = a->valueint;
                *temp = b->valueint;
                
                cJSON_Delete(root);
                return 0;
            }
            else
            {
                cJSON_Delete(root);
                return -1;
            }
        }
        else
        {
            printf("read rpc reply err : %d\n", iLen);
            return -1;
        }
    }
    else
    {
        printf("send rpc request err : %d, %s\n", iLen, strerror(errno));
        return -1;
    }
}

/* 连接RPC Server
 * 返回值: (>0)socket, (-1)失败
 */
int RPC_Client_Init(void) 
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    int iRet;

    iSocketClient = socket(AF_INET, SOCK_STREAM, 0);

    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(PORT);  /* host to net, short */
    //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("127.0.0.1", &tSocketServerAddr.sin_addr);
    memset(tSocketServerAddr.sin_zero, 0, 8);


    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
    if (-1 == iRet)
    {
        printf("connect error!\n");
        return -1;
    }

    return iSocketClient;    
}

static void print_usage(char *exec)
{
    printf("Usage:\n");
    printf("%s led <0|1>\n", exec);
    printf("%s dht11\n", exec);
}

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        print_usage(argv[0]);
        return -1;
    }

    {        
        int sum;
        int fd = RPC_Client_Init();

        if (fd < 0)
        {
            printf("RPC_Client_Init err : %d\n", fd);
            return -1;
        }

        if (argc == 3 && !strcmp(argv[1], "led"))
        {
            int on = (int)strtoul(argv[2], NULL, 0);
            int err = rpc_led_control(fd, on);
            if (!err)
            {
                printf("set led ok\n");
            }
            else
            {
                printf("rpc err : %d\n", err);
            }
        }
        else if (argc == 2 && !strcmp(argv[1], "dht11"))
        {            
            char humi, temp;
            int err = rpc_dht11_read(fd, &humi, &temp);
            if (err)
            {
                printf("rpc err : %d\n", err);
            }
	    else
            {
                 printf("dht11 humi = %d, temp = %d\n", humi, temp);
            }
        }            
    }
    return 0;
}

课程链接

Linux应用入门实验班https://video.100ask.net/p/t_pc/course_pc_detail/video/v_66e049f8e4b023c061203dd5?product_id=p_66e0497de4b023c06c8ea08d&content_app_id=&type=6

相关推荐
wangjialelele7 分钟前
一文读懂 Redis 持久化与事务
linux·数据库·redis·bootstrap
Linux蓝魔11 分钟前
麒麟官方yum源配置V10SP2-V10SP3-V10SP3-2403
大数据·linux·运维
helloliyh12 分钟前
linux 删除指定日期目录(包括目录下文件)
linux·运维·服务器
半个俗人23 分钟前
06.Linux用户权限相关命令
linux·运维·服务器
小宇的天下24 分钟前
Calibre LVS Circuit Comparison(1)
linux·数据库·lvs
涛声依旧3931625 分钟前
构建部署kubernetes所需主机
linux·运维·云原生·容器·kubernetes
淼淼爱喝水36 分钟前
OpenEuler 系统下 Ansible 环境部署与连通性测试完整步骤
linux·开发语言·php·openeuler
KuYouRan39 分钟前
ubuntu22.04用RTX2060显卡玩steam游戏
linux·其他·ubuntu·游戏
叠叠乐1 小时前
linux.service 自起文件want和After意思
linux
Ricky_Theseus1 小时前
SPOOLING 系统详解
linux·服务器·数据库