点击这里查看所有博文
本系列博客所述资料均来自合宙官方
,并不是本人原创(只有博客是自己写的),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
暂时还没写,先凑合用着吧。
至于内部怎么实现的我这里就不讲了,很简单,代码开源,有兴趣的可以自己去看。这里只介绍怎么使用库函数。
使用http服务需要包含#include "http_client.h""
头文件,我们这里只用到了3个函数,分别是:
/**新建一个http的对象,每次调用都会新建一个独立的对象,不会修改原有的对象
当初始化传入的参数不足时,初始化会失败,需要调用http_get_init_state
,查询初始化状态
*@param config: 用户传入的初始化结构体
*@return 新建的http对象
**/
http_client_new
(http_client_config_t *config)/**获取初始化的状态
*@param client:填写http_client_new
返回的对象
*@return : 初始化的状态
**/
http_get_init_state
(http_client_handle_t *client);/**/根据新建的对象,执行相应的操作
*@param client:填写http_client_new
返回的对象
**/
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);
}
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);
}
函数库中做了分包接收(一个请求分多包接收),分段接收(一个文件多次请求,不需要用户处理,库函数全部处理好了,用户只需要在回调函数中转存数据即可)。只会在一次的请求数据全部接收到后才会通知消息处理函数。拿下载文件来说,一个文件可能有好几兆,我在库里面默认设置接收缓存区为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
字节。
这时候我们需要用一下网络调试助手,将下面的数据发送出去,响应头中附带的信息是响应200
,Content-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二次开发:环境搭建
里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束