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映射到外网中去,用的花生壳哈。 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