前言
在学习了这么久的C语言,我一直有一个疑问,C语言如何去调用网络中的一些API呢?为了解决这个问题我翻阅了很多资料,但是内容都很少或者就是直接没有,但也不是一无所获,我找到了一个库,这个库叫做libcurl
,我通过教程下载了一下这个库后打开了man手册研究了一下这个库,并通过这个编写了调用高德地图的API的程序。
一、如何调用网络中的API
本质上调用网络中的API是非常的简单,实际上就是发送HTTP的请求给需要获取的网络API的URL并带参数,然后等待网站将你访问的内容返回回来即可。
比如说调用高德地图的查看天气的API,在使用手册中写得比较清楚,只需要向下面的位置发送你需要请求的内容即可得到你请求的结果
https://restapi.amap.com/v3/weather/weatherInfo?
然后参数填写在?
的后面,一般来说需要传入一个key值还有city值还有extensions值即可:
https://restapi.amap.com/v3/weather/weatherInfo?key=124124&city=1001&extensions=all
即可调用天气的API并得到返回的值。
二、curl库
1.什么是curl库
curl是一个非常流行的开源网络传输库,它支持多种协议,包括HTTP、HTTPS、FTP、SMTP等。使用curl库开发者开源方便的在自己的应用程序中进行网络传输操作,例如发送HTTP请求、下载文件、上传文件等一系列操作,这个curl库是非常的强大的。
2.安装curl库
在初次使用时curl是默认安装的,但这个安装的curl是默认是命令的,如果需要使用到curl库的话还需要我们手动的进行安装,安装时执行下面的命令:
sudo apt-get install libcurl4-openssl-dev
执行完这个命令后会将curl.h
和对应的链接库都添加到/usr/
下的目录中,这样我们可以调用这个库文件继续编写程序了。
3.curl的使用方法
在这里先简单介绍一下curl.h
的使用方法,后面才能更好的书写代码。
3.1 导入库
首先先得导入一下curl.h库
#include <curl/curl.h>
导入后就需要将curl进行初始化。
3.2 初始化curl
在初始化之前需要先创建一个CURL
的句柄,这个句柄是初始化成功的curl,初始化使用的函数是curl_easy_init()
,它会返回CURL*
类型的值,需要使用前面创建的句柄进行接收,如果初始化失败,那会得到NULL
CURL* curl = NULL; curl = curl_easy_init(); if (curl){ // 执行的内容 }
当然,初始化curl后肯定需要进行释放,否则会导致内存的泄露。
3.3 释放curl句柄
释放的函数是curl_easy_cleanup()
curl_easy_cleanup(curl); curl = NULL;
这里需要注意,我的curl
是一个指针,当它里面的内容所对应的空间释放后,需要让它储存的内容为一个NULL,这里可以看我一级指针的基础内容来进行学习。
3.4 配置curl句柄
学习了如何初始化curl和释放后,现在肯定是需要进行使用了,但是现在还是不能够进行使用,因为还需要配置一下初始化的curl请求项,配置使用的函数是curl_easy_setopt(curl, mode, value);,其中curl是我们初始化得到的句柄,mode是设置选项,这里的选项比较多,下面只讲了一些内容:
CURLOPT_URL:设置请求的url
CURLOPT_POSTFIELDS:设置POST请求的参数
CURLOPT_WRITEFUNCTION:设置回调函数,用于请求响应数据
CURLOPT_WRITEDATA:接收回调函数的void*指针,但在使用完需要使用free()函数进行释放
这里只是简单的介绍一下,还有其它的参数,大家如果需要了解可以查找官方手册进行学习。
value是设置内容,有些选项是需要输入内容的。
例如我设置url和回调函数,代码如下
size_t got_data(char* duff, size_t size, size_t nmemd, void* userp){ } curl_easy_setopt(curl, CURLOPT_URL, "https://baidu.com"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, got_data);
其中got_data是回调函数,其中的参数也比较丰富,
第一个参数是请求收到的内容
第二个是接收到的内容的大小
第三个是请求一次收到的数据包个数
第四个参数是一个空指针,一般是用于处理好内容后通过CURLOPT_WRITEDATA选项来获取这个指针的内容。
一般来说,我们在回调函数中通常写下面的代码:
size_t got_data(char* duff, size_t size, size_t nmemd, void* userp){ // 求出一次请求的区块大小 size_t bytes = size * nmemd; //返回请求的区块大小 return bytes; }
3.5 执行curl请求
当执行完后面的内容后就可以发送请求了,发送请求的函数是curl_easy_perform(CURL*)
,这个函数执行后会返回一个CURLcode
类型的错误码,当这个值为CURL_OK
就代表成功,代码如下:
CURLcode ret = curl_easy_perform(curl); if (ret != CURL_OK){ //打印错误信息 sprintf(stderr, "error is %s\n", curl_easy_strerror(ret)); }
3.6 完整代码
对应的makefile文件如下:
三、开始写代码
现在就开始写一下这个代码了,其实代码非常的简单,只需要大家记住下面的步骤即可:
- 初始化curl
- 设置curl中的选项
- 发送请求
- 打印数据
这样就可以完成请求了,完整代码如下:
#include <stdio.h> #include <stdlib.h> #include <curl/curl.h> #include <string.h> #define URL_LENGTH 256 //Url地址 #define DATA_LENGTH 1024 //接收数据的长度 //处理数据回调函数 size_t write_data(char* duffer, size_t size, size_t nmend, void* userp){ char* data = (char*)userp; //将传入的空指针的地址给一个指针 size_t bytes = size * nmend; //求出这个数据包的长度 memcpy(data, duffer, bytes); //将收到的数据交给前面定义的指针 return bytes; //返回数据包的长度 } int main(){ CURL* curl = NULL; CURLcode ret; FILE* fd = NULL; char url[URL_LENGTH]; //请求的url char data[DATA_LENGTH]; //接收的数据 char* key = "你注册的Key值,填自己的"; //key值 char* adcode = "河北"; char* output = "json"; //请求数据的方式 char* extensions = "base"; //请求的时间 //拼接请求的URL地址 snprintf(url, sizeof(url), "https://restapi.amap.com/v3/weather/weatherInfo?key=%s&city=%s&output=%s&extensions=%s", key, adcode, output, extensions); curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); //初始化curl if (curl){ curl_easy_setopt(curl, CURLOPT_URL, url); //请求的url curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //请求的数据放入回调函数中进行处理 curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); //返回出回调函数中的void*指针 ret = curl_easy_perform(curl); //发送数据 if (ret != CURLE_OK){ //失败的情况 fprintf(stderr, "curl_easy_perform feild is %s\n", curl_easy_strerror(ret)); } else{ //将数据打印并写入文件中 printf("%s\n", data); fd = fopen("data.txt", "w"); fwrite(data, 1, sizeof(data), fd); fclose(fd); fd = NULL; } curl_easy_cleanup(curl); } curl_global_cleanup(); return 1; }
其中多了一条curl_global_init(CURL_GLOBAL_DEFAULT);这个语句主要是全局声明了一下curl,基本上不影响,可以加也可以不加。
然后使用sprintf()函数拼接一下字符串,因为key值、city值、EXTENSIONS值都是要用户自己来写的,所以需要对其进行拼接,当然你也可以写死,但是写死是真的傻,这里最好使用我这个方法,当然,也可以使用main函数的参数来指定这个内容,如果你不会,那就算了吧。
在设置的时候我让数据进入到回调函数中,因为发送完请求后会接收数据,但这个数据比较杂,我需要进行处理一下,所以放入回调函数中进行了一次处理,在回调函数中进行的处理主要是将传入的数据放入前面创建好的指针中。
对应的makefile文件如下:
INCLUDE = /usr/include USR = ../include LIB = curl demo:demo.c gcc -o ../bin/demo demo.c -I ${INCLUDE} -I ${USR} -l ${LIB}
执行的效果如下
四、将读取的数据使用json读出
可以看到上面获得的内容太杂了,有没有什么方法可以让它读取的内容清晰一点,就比如向下面一样:
当然是可以的,这需要使用到json来处理这些数据了,因为篇幅有限,我后面会单独出一期C语言操作json数据的内容的,这里直接上代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> #include <json-c/json.h> #define KEY "你的key值" //key值 #define CITY "河北省" //城市 #define EXTENSIONS "base" //请求内容 #define SIZE 128 //curl大小 #define STR 1024 //字符串大小 //设置结构体来存放数据 typedef struct DataStruct{ char* memroy; size_t size; }Data; typedef struct WearStruct{ struct json_object* city; //城市 struct json_object* weather; //天气 struct json_object* temper; //温度 struct json_object* wind; //风方向 struct json_object* hum; //湿度 struct json_object* reporttime; //更新时间 }Wear; //接收数据回调函数 size_t got_data(char* duffer, size_t size, size_t nmend, void* userp){ size_t bytes = size * nmend; //将userp空指针强制转换为Data类型的指针 Data* data = (Data*)userp; //重新为data中的memroy分配空间 /** * 这里加1是因为想要存放\0,所以需要让它+1 */ data->memroy = realloc(data->memroy, data->size + bytes + 1); //判断是否开辟空间成功 if (data->memroy == NULL){ //如果不成功 printf("Not enough memory (realloc returned NULL)\n"); return 0; } //将获取的内容拷贝到data结构体中的memroy中 memcpy(&(data->memroy[data->size]), duffer, bytes); data->size += bytes; //让data中memroy中的最后位置赋予一个0 data->memroy[data->size] = 0; return bytes; } void PrintData(json_object* json, json_object* j_arr_json, Wear* wear){ //提取数据 json_object_object_get_ex(j_arr_json, "city", &(wear->city)); //城市 json_object_object_get_ex(j_arr_json, "weather", &(wear->weather)); //天气 json_object_object_get_ex(j_arr_json, "temperature", &(wear->temper)); //温度 json_object_object_get_ex(j_arr_json, "winddirection", &(wear->wind)); //风的方向 json_object_object_get_ex(j_arr_json, "humidity", &(wear->hum)); json_object_object_get_ex(j_arr_json, "reporttime", &(wear->reporttime)); //打印数据 printf("城市:%s\n天气:%s\n温度:%s\n风向:%s\n湿度:%s\n更新时间:%s\n", json_object_get_string(wear->city), json_object_get_string(wear->weather), json_object_get_string(wear->temper), json_object_get_string(wear->wind), json_object_get_string(wear->hum), json_object_get_string(wear->reporttime)); } int main(){ char url[SIZE], str[STR], buf[STR]; Data main_data; size_t len; //数组的长度 int i; //遍历数组的变量 //创建json句柄 struct json_object* json, *j_info, *j_infocode, *j_arr, *j_arr_json; //实例化储存数据的结构体 Wear wear; //文件句柄 FILE* fd = NULL; //拼接字符串 snprintf(url, SIZE, "https://restapi.amap.com/v3/weather/weatherInfo?key=%s&city=%s&extensions=%s", KEY, CITY, EXTENSIONS); //创建CURL句柄 CURL* curl = NULL; CURLcode ret; //为main_data中的值进行初始化 main_data.memroy = malloc(1); main_data.size = 0; //初始化curl curl = curl_easy_init(); if (curl){ //配置CURL句柄 curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, got_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&main_data); //发送连接 ret = curl_easy_perform(curl); if (ret != CURLE_OK){ fprintf(stderr, "curl_easy_perform feild is %s\n", curl_easy_strerror(ret)); } else{ printf("%s\n", main_data.memroy); fd = fopen("data.json", "w+"); fwrite(main_data.memroy, 1, main_data.size, fd); //关闭文件 fclose(fd); fd = NULL; } } //关闭连接 curl_easy_cleanup(curl); curl = NULL; //处理数据 //打开.json文件 fd = fopen("data.json", "r"); //读取.json文件中的内容 fread(buf, STR, 1, fd); //printf("data.json is:%s\n", buf); fclose(fd); fd = NULL; //将读取文件中的内容转换为json json = json_tokener_parse(buf); //提取json中的内容 json_object_object_get_ex(json, "info", &j_info); json_object_object_get_ex(json, "infocode", &j_infocode); //获取lives json_object_object_get_ex(json, "lives", &j_arr); //读取数组的长度 len = json_object_array_length(j_arr); //将数组转换为json数据并打印出数据 for (i = 0; i < len; i++){ j_arr_json = json_object_array_get_idx(j_arr, 0); PrintData(json, j_arr_json, &wear); } //释放json对象 json_object_put(json); return 1; }
上面输出的内容就直接是之前展示的结果了,但是需要注意几个问题,第一个就是这个代码中使用到了一个data.json的文件作为中转,为什么有这个文件呢?这个就是我瞎加的哈哈哈,当然也可以去掉,如果不会去掉的话直接将文件的模式改为w+即可,这样可以直接创建出一个文件出来
fd = fopen("data.json", "w+");
上面有的话就不用管了,也可以直接手动创建出来一个data.json
文件。
总结
其实使用C语言调用网络API的方法很简单的,大家只需要好好了解一下curl库即可,上面的内容是完整的调用过程,代码可以直接用,只需要改一下key值即可。希望大家能点点赞,这是对我最大的支持!谢谢大家了!