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方式传输的超文本协议。所以用个网络调试助手应该就可以搞定的咯。

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

3.大功告成
现在可以将4G POS设备的刷卡数据通过http上传到服务器咯
在这里插入图片描述

更多推荐