• 欢迎大家分享资料!前往留言板评论即可!

10、RDA8910CSDK二次开发:简易的http通讯库

合宙 模组资料网 2年前 (2021-05-15) 289次浏览 0个评论 扫描二维码
文章目录[隐藏]

目录

点击这里查看所有博文

  本系列博客所述资料均来自合宙官方,并不是本人原创(只有博客是自己写的),csdk只是得到了口头的允许公开授权。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

  本系列博客基于紫光展锐的RDA8910 LTE Cat 1 bis芯片平台开发。理论上适用于合宙的Air720U、Air724U、广和通L610以及安信可的cat-01模块。

  各个厂家的部分配置文件可能不一样,也许会导致设备出现奇怪的问题,其他的模块我也不确定能不能用,自行测试。但是有一点编译下载和监视流程基本一样,可供参考。

  先不管支不支持,如果你用的模块是是紫光展锐的RDA8910,那都不妨一试,也许会有意外收获(也有可能变砖,慎重!!!)。

  我使用的是Air724UG开发板,如果在其他模块上不能用,那也不要强怼,也许是开发包不兼容吧。这里的代码是没有问题的。例程仅供参考!

一、前言

  有好几天没写博客了,几天前刚说了tcp通讯和udp通讯。本来准备写一下http协议怎么使用,在官方开发包里找了半天没找到有关http的库函数。后来通过其他渠道确认了官方暂时还没有提供有关http的相关库函数。也就是说除非自己写或者自己移植,不然就没法用。
在这里插入图片描述
  随后想了下,干脆移植一个开源库试试吧,也许运气好移植成功了呢。花了半天时间找了几个开源的http库,打开一看实在是太复杂了,以我的能力暂时是搞不定。看的我头晕眼花,果断放弃。
在这里插入图片描述
  其实我对这个库的要求也不高,支持以下简单的post,get。然后能下载稍大的文件就行了。如果有https那就更好了,没有也不碍事。既然移植不成那就自己写一个简单的吧,凑合用一下。过一段时间也许官方的库就出来了。

  http说到底其实也就是一个tcp连接,发送指定的报文信息而已,简单的应用并不复杂。只要在网上找一个协议报文格式,对着的信息上面直接替换即可。

  下面就是一个GET请求的报文格式,用的是HTTP/1.1协议,注意HTTP/1.1支持长连接,但是这个长连接并不是永久的长连接。在短时间内,可以允许客户端向服务器连续发送多个请求,而无需重新建立连接。在空闲时(一段时间没有动作)服务器会主动断开与客户端的连接。

GET /admin_ui/rdx/core/images/close.png HTTP/1.1
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: xxx.xxx.xxx.xxx
Connection: Keep-Alive
Accept: */*

二、编写测试程序

  花了几天时间,根据报文的格式要求,写了一个简易的http函数库,目前只支持GET请求、POST请求。可以支持下载一些不是特别大的文件,昨晚测试下载800kb的文件没遇到什么问题。https暂时还没写,先凑合用着吧。

2.1、了解本例程所用到的函数

  至于内部怎么实现的我这里就不讲了,很简单,代码开源,有兴趣的可以自己去看。这里只介绍怎么使用库函数。
  使用http服务需要包含#include "http_client.h""头文件,我们这里只用到了3个函数,分别是:

/**新建一个http的对象,每次调用都会新建一个独立的对象,不会修改原有的对象
当初始化传入的参数不足时,初始化会失败,需要调用http_get_init_state,查询初始化状态
*@param config: 用户传入的初始化结构体
*@return 新建的http对象
**/

  • http_client_handle_t http_client_new(http_client_config_t *config)

/**获取初始化的状态
*@param client:填写http_client_new返回的对象
*@return : 初始化的状态
**/

  • http_client_init_state http_get_init_state(http_client_handle_t *client);

/**/根据新建的对象,执行相应的操作
*@param client:填写http_client_new返回的对象
**/

  • void http_client_perform(http_client_handle_t *client);

2.2、编写get请求测试程序

  demo中我写了两个get请求的测试,两个url只要选一个就行了,demo写的比较简单,一次只能测试一个请求。

        http_client_config_t config = {0};
        //////////////GET测试1/////////////////////////
        config.url = "https://bkimg.cdn.bcebos.com/pic/37d3d539b6003af356370ff93f2ac65c1138b69e?x-bce-process=image/resize,m_lfit,w_268,limit_1/format,f_jpg";
        //////////////////////////////////////////////////

        //////////////GET测试2/////////////////////////
        //config.url = "https://i0.hdslb.com/bfs/album/8ac5f3eecccb17dfe8702eff13a4fb51d59cfc4b.jpg";
        config.method = HTTP_METHOD_GET;
        config.event_handler = http_cb;
        //创建对象
        client_handle_t = http_client_new(&config);
        iot_debug_print("[http_client] init_state:%d", http_get_init_state(&client_handle_t));
        if (http_get_init_state(&client_handle_t) == HTTP_INIT_OK)
        {
            http_client_perform(&client_handle_t);
        }

2.3、编写post请求测试程序

  post请求主要是提交数据,我这里向http://httpbin.org/post提交字符串”123456“,该网站会回应相应的的报文数据。

        ///////////////////POST测试////////////////////////
        config.url = "http://httpbin.org/post";
        config.table_data = "123456";
        config.method = HTTP_METHOD_POST;
        ////////////////////////////////////
        config.event_handler = http_cb;
        //创建对象
        client_handle_t = http_client_new(&config);
        iot_debug_print("[http_client] init_state:%d", http_get_init_state(&client_handle_t));
        if (http_get_init_state(&client_handle_t) == HTTP_INIT_OK)
        {
            http_client_perform(&client_handle_t);
        }

2.4、编写消息回调事件

  函数库中做了分包接收(一个请求分多包接收),分段接收(一个文件多次请求,不需要用户处理,库函数全部处理好了,用户只需要在回调函数中转存数据即可)。只会在一次的请求数据全部接收到后才会通知消息处理函数。拿下载文件来说,一个文件可能有好几兆,我在库里面默认设置接收缓存区为10240字节(10KB),用户可以自行更改,一次最多接收10KB的数据。由于网络问题,这10KB的数据可能需要进行分包接收,回调函数只会在10KB的数据全部接收完毕后才会通过回调函数通知用户,转存数据。

void http_cb(http_client_event *evt)
{
    iot_os_sleep(1000);//这里需要一个延时,不然下载的过程中,打印速度太快,日志会丢失,不需要查看日志的话可以忽略
    iot_debug_print("[http_client] http_client_event_state:%d", evt->state);
    iot_debug_print("[http_client] respons_state:%s", evt->respons_state);
    iot_debug_print("[http_client] data:%s", evt->data);
    iot_debug_print("[http_client] datalen:%d", evt->datalen);
}

三、编译并下载程序

  完整代码在这,自取。

/*
 * @Author: your name
 * @Date: 2020-05-19 14:05:32
 * @LastEditTime: 2020-05-31 17:53:31
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \RDA8910_CSDK\USER\user_main.c
 */

#include "string.h"
#include "cs_types.h"

#include "osi_log.h"
#include "osi_api.h"

#include "am_openat.h"
#include "am_openat_vat.h"
#include "am_openat_common.h"

#include "iot_debug.h"
#include "iot_uart.h"
#include "iot_os.h"
#include "iot_gpio.h"
#include "iot_pmd.h"
#include "iot_adc.h"
#include "iot_vat.h"
#include "iot_network.h"
#include "iot_socket.h"

#include "http_client.h"

HANDLE TestTask_HANDLE = NULL;
uint8 NetWorkCbMessage = 0;
int socketfd = -1;

http_client_handle_t client_handle_t = {0};

void http_cb(http_client_event *evt)
{
    iot_os_sleep(1000);//这里需要一个延时,不然下载的过程中,打印速度太快,日志会丢失,不需要查看日志的话可以忽略
    iot_debug_print("[http_client] http_client_event_state:%d", evt->state);
    iot_debug_print("[http_client] respons_state:%s", evt->respons_state);
    iot_debug_print("[http_client] data:%s", evt->data);
    iot_debug_print("[http_client] datalen:%d", evt->datalen);
}
static void TestTask(void *param)
{
    bool NetLink = FALSE;
    while (NetLink == FALSE)
    {
        T_OPENAT_NETWORK_CONNECT networkparam = {0};
        switch (NetWorkCbMessage)
        {
        case OPENAT_NETWORK_DISCONNECT: //网络断开 表示GPRS网络不可用澹,无法进行数据连接,有可能可以打电话
            iot_debug_print("[socket] OPENAT_NETWORK_DISCONNECT");
            iot_os_sleep(10000);
            break;
        case OPENAT_NETWORK_READY: //网络已连接 表示GPRS网络可用,可以进行链路激活
            iot_debug_print("[socket] OPENAT_NETWORK_READY");
            memcpy(networkparam.apn, "CMNET", strlen("CMNET"));
            //建立网络连接,实际为pdp激活流程
            iot_network_connect(&networkparam);
            iot_os_sleep(500);
            break;
        case OPENAT_NETWORK_LINKED: //链路已经激活 PDP已经激活,可以通过socket接口建立数据连接
            iot_debug_print("[socket] OPENAT_NETWORK_LINKED");
            NetLink = TRUE;
            break;
        }
    }
    if (NetLink == TRUE)
    {
        http_client_config_t config = {0};
        //////////////GET测试1/////////////////////////
        config.url = "https://bkimg.cdn.bcebos.com/pic/37d3d539b6003af356370ff93f2ac65c1138b69e?x-bce-process=image/resize,m_lfit,w_268,limit_1/format,f_jpg";
        config.method = HTTP_METHOD_GET;
        //////////////////////////////////////////////////

        //////////////GET测试2/////////////////////////
        //config.url = "https://i0.hdslb.com/bfs/album/8ac5f3eecccb17dfe8702eff13a4fb51d59cfc4b.jpg";
        //config.method = HTTP_METHOD_GET;
        //////////////////////////////////////////////////

        ///////////////////POST测试////////////////////////
        //config.url = "http://httpbin.org/post";
        //config.table_data = "123456";
        //config.method = HTTP_METHOD_POST;
        ////////////////////////////////////
        config.event_handler = http_cb;
        //创建对象
        client_handle_t = http_client_new(&config);

        iot_debug_print("[http_client] init_state:%d", http_get_init_state(&client_handle_t));
        if (http_get_init_state(&client_handle_t) == HTTP_INIT_OK)
        {
            http_client_perform(&client_handle_t);
        }
    }
    iot_os_delete_task(TestTask_HANDLE);
}
static void NetWorkCb(E_OPENAT_NETWORK_STATE state)
{
    NetWorkCbMessage = state;
}
//main函数
int appimg_enter(void *param)
{
    //系统休眠
    iot_os_sleep(10000);
    //注册网络状态回调函数
    iot_network_set_cb(NetWorkCb);
    //创建一个任务
    //TestTask_HANDLE =
    TestTask_HANDLE = iot_os_create_task(TestTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "TestTask");
    return 0;
}

//退出提示
void appimg_exit(void)
{
    OSI_LOGI(0, "application image exit");
}


四、分析结果

  测试GET请求,通过打印数据,看到Range_all=25644字节,也就是图片的总大小25.6KB。分三段接收,第一段接收10240,第二段接收10240,第三段接收5164,三段加在一起正好是25644
在这里插入图片描述
  测试POST请求,通过打印数据,http响应200,只能看到部分接收到的数据,还有数据大小是495字节。
10、RDA8910CSDK二次开发:简易的http通讯库
  这时候我们需要用一下网络调试助手,将下面的数据发送出去,响应头中附带的信息是响应200Content-Length=496,以及正文数据的第一行和我们在日志中看到的是一样的。数据应该是没什么问题,我也懒得将数据一行行拆开打印了测试了。

POST /post HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36\r\nContent-Length: 8\r\nContent-Type: application/json\r\nAccept: */*\r\n\r\n123456\r\n

在这里插入图片描述

五、总结

  本人提供的函数包内,有如下接口可供使用。

//修改host数据,必须先创建一个client对象才可以修改
void http_set_host(http_client_handle_t *client, char *host);
//获取host数据
char *http_get_host(http_client_handle_t *client);

//修改ip数据,必须先创建一个client对象才可以修改
void http_set_ip(http_client_handle_t *client, char *ip);
//获取ip数据
char *http_get_ip(http_client_handle_t *client);

//修改port数据,必须先创建一个client对象才可以修改
void http_set_port(http_client_handle_t *client, int port);
//获取port数据
int http_get_port(http_client_handle_t *client);

//修改path数据,必须先创建一个client对象才可以修改
void http_set_path(http_client_handle_t *client, char *path);
//获取path数据
char *http_get_path(http_client_handle_t *client);


//修改table_data数据,必须先创建一个client对象才可以修改
void http_set_table_data(http_client_handle_t *client, char *table_data);
//获取table_data数据
char *http_get_table_data(http_client_handle_t *client);

//修改method数据,必须先创建一个client对象才可以修改
void http_set_method(http_client_handle_t *client, http_client_method_t method);
//获取method数据
http_client_method_t http_get_method(http_client_handle_t *client);

//修改event_handler数据,必须先创建一个client对象才可以修改
void http_set_event_handler(http_client_handle_t *client, http_event_handle_cb event_handler);


//删除http对象
void http_delete(http_client_handle_t *client);
//获取初始化状态
http_client_init_state http_get_init_state(http_client_handle_t *client);
//新建一个http的对象
http_client_handle_t http_client_new(http_client_config_t *config);
//根据新建的对象,执行相应的操作
void http_client_perform(http_client_handle_t *client);

  一般情况下只需要用到如下4个函数即可满足简单的使用需求。复杂的功能我也没写,其他的接口主要是修改http对象内部变量的值,也可以直接通过指针修改(不推荐),容易出现问题。

//删除http对象
void http_delete(http_client_handle_t *client);
//获取初始化状态
http_client_init_state http_get_init_state(http_client_handle_t *client);
//新建一个http的对象
http_client_handle_t http_client_new(http_client_config_t *config);
//根据新建的对象,执行相应的操作
void http_client_perform(http_client_handle_t *client);

不会下载的点击这里,进去查看我的RDA8910 CSDK二次开发入门教程专题第一篇博文1、RDA8910CSDK二次开发:环境搭建里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束


喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址