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.代码分析

老样子,直接上代码吧

  1. T_HTTPSND_STATE slim_http_handler(char *URL, char *msg, uint16_t msg_len){
  2.         sdk_uart_printf("\n++++++++++++++++++++ HTTP handler ++++++++++++++++++++\n");
  3.         struct http_client *client = NULL;
  4.         struct http_data_s *client_data = NULL;
  5.         struct http_client_list * header = NULL;
  6.         int resp_code = 0;
  7.         T_HTTPSND_STATE sta = HTTPSND_OK;
  8.         uint16_t i = 0;
  9.         /* 申请内存空间 */
  10.         client_data = malloc(sizeof(*client_data));
  11.         if(!client_data){
  12.                 sdk_uart_printf("malloc failed\n");
  13.                 free(client_data);
  14.                 return HTTPSND_MEMORY_FAILED;
  15.         }
  16.         memset(client_data, 0, sizeof(*client_data));
  17.         /*初始化客户端*/
  18.         client = http_client_init();
  19.         if(!client){
  20.                 sdk_uart_printf("client failed\n");
  21.                 free(client_data);
  22.                 http_client_shutdown(client);
  23.                 return HTTPSND_CLIENT_FAILED;
  24.         }
  25.         http_client_setopt(client, HTTPCLIENT_OPT_URL, URL);//http服务器地址
  26.         http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB, _response_cb); //响应回调
  27.         http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB_DATA, client_data);
  28.         http_client_setopt(client, HTTPCLIENT_OPT_METHOD, HTTPCLIENT_REQUEST_POST);        /*设置模式,支持 GET/POST/PUT*/       
  29.        
  30.         http_client_setopt(client, HTTPCLIENT_OPT_POSTLENGTH, msg_len);//strlen("data=1234567890"));        /*http context length*/
  31.         http_client_setopt(client, HTTPCLIENT_OPT_POSTDATA, msg);//"data=1234567890");                                        /*post data is http context*/
  32.        
  33.         //添加 HTTP header
  34.         header = http_client_list_append(header, "Content-Type: application/x-www-form-urlencoded\r\n");        //内容为表单数据
  35.         header = http_client_list_append(header, "Accept: text/html, */* \r\n");        //可接收内容类型
  36.         header = http_client_list_append(header, "Connection: Keep-Alive\r\n");        //keep alive
  37.         http_client_setopt(client, HTTPCLIENT_OPT_HTTPHEADER, header);        //设置header
  38.        
  39.         http_client_perform(client);
  40.         http_client_getinfo(client, HTTPCLIENT_GETINFO_RESPONSE_CODE, &resp_code);        //获取响应代码
  41.         sdk_uart_printf("[http_client_test]Get tcp state %d\n", resp_code);
  42.         if (resp_code >= 200 && resp_code < 300){
  43. #if 0
  44.                 if(client_data->data_sz){
  45.                         sdk_uart_printf("\r\n data_sz=%u, %s", client_data->data_sz,client_data->data);
  46.                         sdk_uart_printf("\r\nresult:");
  47.                         for (i = 0; i < 100; i++){
  48.                                 sdk_uart_printf("%02x ",client_data->data[i]);
  49.                         }
  50.                 }
  51. #endif
  52.         }else if (resp_code == 404) {
  53.                 sdk_uart_printf("response_code == %d\r\n%s",resp_code ,client_data->data);
  54.                 sta = HTTPSND_404_ERROR;
  55.         }
  56.         free(client_data);
  57.         http_client_shutdown(client);
  58.         sdk_uart_printf("\n-------------------- HTTP handler --------------------\n");
  59.         return sta;
  60. }
  61. static int _response_cb(char *buffer, int size, int nitems, void *private_data){
  62.         struct http_data_s *client_data = private_data;
  63.         sdk_uart_printf("recv data_sz=%u, %s\n",size,buffer);
  64.         if ((client_data->data_sz + size) < sizeof(client_data->data)) {
  65.                 memcpy(client_data->data + client_data->data_sz, buffer, size);
  66.                 client_data->data_sz += size;
  67.                 return 0;
  68.         }
  69.         return -1;
  70. }
3.调试

一方面,自己不是太懂后端,另一方面因为http是承载与TCP方式传输的超文本协议。所以用个网络调试助手应该就可以搞定的咯。


  • 搭建环境
    4G模块并不能访问内网,但是有没有现成的外网服务器,肿么办呢,那就把内网IP映射到外网中去,用的花生壳哈。 watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NsaW1tbQ==,size_16,color_FFFFFF,t_70.jpg
  • watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NsaW1tbQ==,size_16,color_FFFFFF,t_70.jpg 分析数据
  • 最后根据网络调试助手接收到的http工具发送的数据和4G模块发送的数据进行分析,最后确定为header没有设置正确,添加header的Content-Type为表单类型即可咯

    3.大功告成
    现在可以将4G POS设备的刷卡数据通过http上传到服务器咯

    watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NsaW1tbQ==,size_16,color_FFFFFF,t_70.jpg