目录
前言
学习的课程是百问网韦东山老师的课程,对更详细步骤感兴趣的同学,可以去学习视频课程。代码里的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;
}