1.Http简介
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写。它的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的RFC,RFC 1945定义了HTTP/1.0版本。其中最著名的就是RFC 2616。RFC 2616定义了今天普遍使用的一个版本——HTTP 1.1。
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议
一般的,http运行于TCP/IP协议之上,默认端口为80
运行流程
HTTP工作过程可分为四步:
1)首先客户机与服务器需要建立连接。
2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。
如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
一般的,嵌入式设备很少用http和https通信,很大方面受限于RAM。更多采用TCP/IP、CoAP、MQTT、UDP…等轻量化协议进行通信,本次记录一下在RV3 4G模块下的http通信。
2.代码分析
老样子,直接上代码吧
T_HTTPSND_STATE slim_http_handler(char *URL, char *msg, uint16_t msg_len){
sdk_uart_printf("\n++++++++++++++++++++ HTTP handler ++++++++++++++++++++\n");
struct http_client *client = NULL;
struct http_data_s *client_data = NULL;
struct http_client_list * header = NULL;
int resp_code = 0;
T_HTTPSND_STATE sta = HTTPSND_OK;
uint16_t i = 0;
/* 申请内存空间 */
client_data = malloc(sizeof(*client_data));
if(!client_data){
sdk_uart_printf("malloc failed\n");
free(client_data);
return HTTPSND_MEMORY_FAILED;
}
memset(client_data, 0, sizeof(*client_data));
/*初始化客户端*/
client = http_client_init();
if(!client){
sdk_uart_printf("client failed\n");
free(client_data);
http_client_shutdown(client);
return HTTPSND_CLIENT_FAILED;
}
http_client_setopt(client, HTTPCLIENT_OPT_URL, URL);//http服务器地址
http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB, _response_cb); //响应回调
http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB_DATA, client_data);
http_client_setopt(client, HTTPCLIENT_OPT_METHOD, HTTPCLIENT_REQUEST_POST); /*设置模式,支持 GET/POST/PUT*/
http_client_setopt(client, HTTPCLIENT_OPT_POSTLENGTH, msg_len);//strlen("data=1234567890")); /*http context length*/
http_client_setopt(client, HTTPCLIENT_OPT_POSTDATA, msg);//"data=1234567890"); /*post data is http context*/
//添加 HTTP header
header = http_client_list_append(header, "Content-Type: application/x-www-form-urlencoded\r\n"); //内容为表单数据
header = http_client_list_append(header, "Accept: text/html, */* \r\n"); //可接收内容类型
header = http_client_list_append(header, "Connection: Keep-Alive\r\n"); //keep alive
http_client_setopt(client, HTTPCLIENT_OPT_HTTPHEADER, header); //设置header
http_client_perform(client);
http_client_getinfo(client, HTTPCLIENT_GETINFO_RESPONSE_CODE, &resp_code); //获取响应代码
sdk_uart_printf("[http_client_test]Get tcp state %d\n", resp_code);
if (resp_code >= 200 && resp_code < 300){
#if 0
if(client_data->data_sz){
sdk_uart_printf("\r\n data_sz=%u, %s", client_data->data_sz,client_data->data);
sdk_uart_printf("\r\nresult:");
for (i = 0; i < 100; i++){
sdk_uart_printf("%02x ",client_data->data[i]);
}
}
#endif
}else if (resp_code == 404) {
sdk_uart_printf("response_code == %d\r\n%s",resp_code ,client_data->data);
sta = HTTPSND_404_ERROR;
}
free(client_data);
http_client_shutdown(client);
sdk_uart_printf("\n-------------------- HTTP handler --------------------\n");
return sta;
}
static int _response_cb(char *buffer, int size, int nitems, void *private_data){
struct http_data_s *client_data = private_data;
sdk_uart_printf("recv data_sz=%u, %s\n",size,buffer);
if ((client_data->data_sz + size) < sizeof(client_data->data)) {
memcpy(client_data->data + client_data->data_sz, buffer, size);
client_data->data_sz += size;
return 0;
}
return -1;
}
3.调试
一方面,自己不是太懂后端,另一方面因为http是承载与TCP方式传输的超文本协议。所以用个网络调试助手应该就可以搞定的咯。
- 搭建环境
4G模块并不能访问内网,但是有没有现成的外网服务器,肿么办呢,那就把内网IP映射到外网中去,用的花生壳哈。
2.分析数据
最后根据网络调试助手接收到的http工具发送的数据和4G模块发送的数据进行分析,最后确定为header没有设置正确,添加header的Content-Type为表单类型即可咯
3.大功告成
现在可以将4G POS设备的刷卡数据通过http上传到服务器咯