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

相关推荐
TracyGC35 分钟前
coco(json)、yolo(txt)、voc(xml)标注格式的相互转换
xml·yolo·json
Aukum40 分钟前
Vulnhub:Cybero1
linux·笔记·安全·web安全·网络安全
兴趣互联1 小时前
centos8 升级openssh-9.8p1
linux
wht65871 小时前
Linux--基本指令
linux·运维·服务器·c语言·开发语言·jvm·c++
浊尘1 小时前
在Kali Linux中使用VNC和iptables配置xrdp以实现远程连接
linux·运维·服务器
iot_qizu1 小时前
Ubuntu环境切换到服务器某个用户后source等命令和Tab快捷补全都用不了了,提示没找到,但root用户可以
linux·运维·服务器·ubuntu
暗恋 懒羊羊1 小时前
Linux 线程互斥
linux·ubuntu
Narnat1 小时前
linux项目_c语言:Makefile编写、动态库生成、添加动态库路径
linux·运维·c语言
2c237c61 小时前
解决远程连接AlpineLinux Mysql/MariaDB 无法连接的问题
linux·mysql·mariadb
xjjeffery2 小时前
文件和目录
linux·c语言·后端