基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)

近期应项目要求,需要在嵌入式设备上实现将数据上传至服务器,查找了许多资料,现在将整个过程所需要的知识整理下来,以备后续查找。

硬件环境

下位机:基于Linux的嵌入式开发硬件
上位机:web服务器(PC端)

HTTP协议简介

HTTP即Hyper Text Transfer Protocol (超文本传输协议),是一种基于TCP/IP通信协议来传递数据 (HTML 文件,图片文件,查询结果等)。

HTTP协议的几种请求方法

在HTTP1.1中,总共有8种请求方法(也叫动作),用来表明对Request-URL指定的资源的不同操作方式。HTTP协议的几种请求方法如下表所示:
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)
HTTP服务器至少应该实现GET和HEAD/POST方法,其他方法都是可选的,此外除上述方法,特定的HTTP服务器支持扩展自定义的方法。大部分时候,需要使用的只有GET和POST。针对项目设计的C语言代码主要实现的功能即为上述方法的POST功能。

四种常见的 POST 提交数据方式

在使用post,put,delete,options等方法的时候,他们都有方法体body,用来存储数据。而body的类型可以有:form-data、x-www-form-urlencoded、raw、binary

form-data

就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来表名文件类型;content-disposition,用来说明字段的一些信息;由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。

x-www-form-urlencoded

这是最常见的POST提交数据的方式了。原生的form表单,如果不设置enctype属性,最终就是以application/x-www-form-urlencoded方式提交数据。会将表单内的数据转换为键值对,比如,name=java&age = 23。

raw

可以上传任意格式的文本,可以上传text、json、xml、html等。

binary

相当于Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。

而本次由于项目需要,使用的是json方式传递数据。

实现思路

由于HTTP协议是基于TCP通信的,故可以有如下多种方式实现HTTP传输协议:
1)使用DTU
2)使用AT命令
3)第三方开源库
4)使用socket通信

本次使用第三方库curl来实现,curl是一个功能强大的网络工具,它能够通过http、ftp等方式下载文件,也能够上传文件。这类库语句简单,能够省去许多开发的时间,提高开发效率。

实现过程

安装wireshark抓包软件

wireshark用于调试http协议和分析http原始数据。
wireshark软件的下载链接如下:
http://soft.wuseng.net/11/3438.html?tab=5752682

确定抓包过程中,上位机或下位机的IP地址

下位机地址:
按win+R打开系统运行界面,输入cmd打开系统终端,在终端中输入指令ipconfig查看本机IP地址:
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)
上位机地址:
由于本次项目使用已知的服务器,所以此处的IP地址是已知的。若需要向一个网站上传数据,也可以通过终端ping+网站域名的方式,得到网站的IP地址。如CSDN的IP地址为101.200.35.175。
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)

设置过滤条件

上一步中已经知道了上位机和下位机的IP地址,即可在wireshark的filter一栏中输入以下过滤条件:
http and ip.addr == 192.168.1.110 and tcp.port == 80
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)
http:指定网络协议
ip.addr == 192.168.1.110:指定服务器ip地址,请根据实际情况替换。
tcp.port == 8080,指定端口号,请根据实际情况替换。

安装JSON包和CURL工具

json安装包链接:https://pan.baidu.com/s/1BDc2JldxTEmmcxwdqesvTw
提取码:54jg

sudo tar -xvf tar xvf json-c-0.9.tar.gz
cd  xvf json-c-0.9.tar.gz
./configure
make
sudo make install

安装curl

sudo apt install curl
sudo apt install libcurl4-openssl-dev

编写HTTP传输代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "/usr/local/include/json/json.h"
 
#define POSTURL    "xxx.xxx.xxx.xxx" 	//需要接收消息的IP地址
 
int main(int argc,char *argv[])
{
	//1、创建一个json数组对象,就可以理解为外面那个大容器[]
	struct json_object *arr=json_object_new_array();

	//2、创建两个字符串对象,可以以理解为[]中的两个小容器{}
	struct json_object *str1=json_object_new_object();
	//struct json_object *str2=json_object_new_object();

	//3、把要存放的数据转为对象
	struct json_object *value1=json_object_new_int(1);
	struct json_object *value2=json_object_new_int(1);
	struct json_object *value3=json_object_new_int(1);
	struct json_object *value4=json_object_new_int(1);
	struct json_object *value5=json_object_new_double(118.846597);
	struct json_object *value6=json_object_new_double(32.027024);
	struct json_object *value7=json_object_new_double(12.18);

	//4、把数值对象添加到字符串对象中
	json_object_object_add(str1,"carId", value1);
	json_object_object_add(str1,"carType", value2);
	json_object_object_add(str1,"carStatus", value3);
	json_object_object_add(str1,"driveStatus", value4);
	json_object_object_add(str1,"longitude", value5);
	json_object_object_add(str1,"latitude", value6);
	json_object_object_add(str1,"carSpeed", value7);

	//5、把数组对象转为字符流进行发送
	const char *temp=json_object_to_json_string(str1);
	
	CURL *curl;
	CURLcode res;
 
	struct curl_slist *http_header = NULL;
 
	curl = curl_easy_init();
	if (!curl)
	{
		fprintf(stderr,"curl init failed\n");
		return -1;
	}
 
        curl_easy_setopt(curl,CURLOPT_URL,POSTURL); //url地址
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); //不检查ssl,可访问https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);  //不检查ssl,可访问https
        curl_easy_setopt(curl,CURLOPT_POSTFIELDS,temp); //post参数
        curl_easy_setopt(curl,CURLOPT_POST,1); //设置问非0表示本次操作为post
        curl_easy_setopt(curl,CURLOPT_VERBOSE,1); //打印调试信息
 
        http_header = curl_slist_append(NULL, "Content-Type:application/json;charset=UTF-8");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header);
 
        curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); //设置为非0,响应头信息location
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);//接收数据时超时设置,如果10秒内数据未
  
	res = curl_easy_perform(curl);
 
	if (res != CURLE_OK)
	{
		switch(res)
		{
			case CURLE_UNSUPPORTED_PROTOCOL:
				fprintf(stderr,"不支持的协议,由URL的头部指定\n");
			case CURLE_COULDNT_CONNECT:
				fprintf(stderr,"不能连接到remote主机或者代理\n");
			case CURLE_HTTP_RETURNED_ERROR:
				fprintf(stderr,"http返回错误\n");
			case CURLE_READ_ERROR:
				fprintf(stderr,"读本地文件错误\n");
			default:
				fprintf(stderr,"返回值:%d\n",res);
		}
		return -1;
	}
 
	curl_easy_cleanup(curl);
	printf("\n");
	return 0;
}

编译:

gcc http.c -o http -ljson -lcurl

接收消息测试与分析

输入过滤条件,点击apply,并运行以上代码,可以抓取到两个数据包:
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)
分别是下位机发送的HTTP请求和上位机发回的HTTP响应。Source一栏表示发送方的IP地址,Destination一栏表示接收方也就是服务器的IP地址,Protocol表示遵循的协议,Length表示数据长度,Info表示数据内容。
在任意数据包上右键点击“追踪流”、“HTTP流”,可以看到发送与接收到的消息。
基于Linux的嵌入式设备通过CURL实现HTTP POST方式向服务器传递JSON数据(C语言)(原理+实现过程+代码)
至此,HTTP发送测试完成。

参考博客

https://blog.csdn.net/songfeihu0810232/article/details/54892149
https://blog.csdn.net/weixin_42399752/article/details/94744785
https://blog.csdn.net/xukai871105/article/details/31008635

上一篇:[安洵杯 2019]easy_web 1


下一篇:HTTP协议实现libcurl库