如今,使用 Web API 是在应用程序之间交换数据的常见做法。关于使用 JavaScript、Python 或 PHP 等语言的 API 的教程有很多,但 C(通常与系统级编程相关)很少被考虑用于此目的。然而,C 完全有能力处理 API 请求,使其成为销售点 (PoS) 系统、物联网设备或嵌入式应用程序等场景的可行选择,在这些场景中,C 已经因其效率和低级控制而被使用。
本文探讨了如何利用 libcurl 库使用 C 语言的 API。最后,您将了解如何使用 C 从 API 获取和处理数据,以及为什么这种方法即使在现代开发中也有意义。
为什么使用 C 来使用 API?
虽然高级语言主导 Web 开发,但 C 仍然是在特定用例中使用 API 的实用选择:
- 性能:C 提供高性能和最小的开销,使其适合物联网设备等资源受限的环境。
- 控制:直接内存管理允许微调优化,特别是对于嵌入式系统。
- 互操作性:C 的广泛使用意味着它可以与系统级操作很好地集成,例如控制硬件、传感器或其他外设。
- 寿命长:用 C 语言构建的应用程序通常具有很长的寿命,尤其是在零售或制造等行业。
libcurl 简介:C 语言 HTTP 工具
要使用 C 语言的 API,libcurl 是首选库。它是一个开源、可移植且功能丰富的库,用于处理通过 HTTP、HTTPS、FTP 等的网络请求。它支持:
- 发出 GET、POST 和其他 HTTP 请求。
- 处理标头和身份验证。
- 高效处理回复。
使用 C 语言 API 的基本步骤
让我们逐步了解使用 C 语言使用 API 的过程,重点关注获取 JSON 数据的实际示例。
设置和安装
要使用 libcurl,您需要在系统上安装它。对于大多数 Linux 发行版,这可以通过以下方式完成:
sudo apt-get install libcurl4-openssl-dev
登录后复制
登录后复制
登录后复制
登录后复制
在 Windows 上,您可以从 libcurl 网站下载预编译的二进制文件:https://curl.se/download.html
在 macOS 上,如果您使用 Homebrew,可以通过
安装它
构建你的 C 程序
从 API 获取数据的简单 C 程序涉及以下组件:
- 正在初始化 libcurl。
- 配置 API 请求(URL、HTTP 方法、标头等)。
- 接收并存储响应。
- 清理资源。
这是一个从公共 API 获取 JSON 数据的示例程序:
sudo apt-get install libcurl4-openssl-dev
登录后复制
登录后复制
登录后复制
登录后复制
运行步骤
将代码保存在文件中,例如 get.c。
使用以下命令编译它:
运行编译好的程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
// Struct to hold response data
struct Memory {
char *response;
size_t size;
};
// Callback function to handle the data received from the API
static size_t ResponseCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t totalSize = size * nmemb;
struct Memory *mem = (struct Memory *)userp;
printf(". %zu %zu\n", size, nmemb);
char *ptr = realloc(mem->response, mem->size + totalSize + 1);
if (ptr == NULL) {
printf("Not enough memory to allocate buffer.\n");
return 0;
}
mem->response = ptr;
memcpy(&(mem->response[mem->size]), contents, totalSize);
mem->size += totalSize;
mem->response[mem->size] = '<pre class="brush:php;toolbar:false">gcc get.c -o get -lcurl
登录后复制
登录后复制
';
return totalSize;
}
int main() {
CURL *curl;
CURLcode res;
struct Memory chunk;
chunk.response = malloc(1); // Initialize memory
chunk.size = 0; // No data yet
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
// Set URL of the API endpoint
char access_token[] = "your-access-token";
char slug[] = "home";
char version[]= "draft";
char url[256];
snprintf(url, sizeof(url), "https://api.storyblok.com/v2/cdn/stories/%s?version=%s&token=%s", slug, version, access_token);
// Print the URL
printf("URL: %s\n", url);
// initializing libcurl
// setting the URL
curl_easy_setopt(curl, CURLOPT_URL, url );
// Follow redirect
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// Set callback function to handle response data
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ResponseCallback);
// Pass the Memory struct to the callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
// Perform the HTTP GET request
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
printf("Response data size: %zu\n", chunk.size);
//printf("Response data: \n%s\n", chunk.response);
}
// Cleanup
curl_easy_cleanup(curl);
}
// Free allocated memory
free(chunk.response);
curl_global_cleanup();
return 0;
}
使用 libcurl 了解 HTTP 响应中的回调机制
在 C 中使用 libcurl 处理 HTTP 响应时,了解回调函数的行为非常重要。您定义的用于处理响应数据的回调函数(例如 ResponseCallback 函数)可能会针对单个 HTTP 响应调用多次。这就是为什么以及它是如何工作的。
为什么回调会被多次调用?
libcurl 中的回调机制旨在高效、灵活地处理数据。 libcurl 不会在处理整个响应之前等待下载整个响应,而是以较小的块处理响应,并在收到每个块时调用回调函数。
此行为允许:
- 高效的内存使用:通过增量处理块,您无需为整个响应预先分配大块内存。
- 流式处理:您可以在每个块到达时对其进行处理或操作,这对于流式传输大型响应或实时处理数据非常有用。
它是如何工作的?
每次从服务器接收到一块数据时,libcurl 都会调用您的回调函数。每个块的大小取决于网络条件、缓冲区大小和 libcurl 的内部逻辑。
回调必须累积块,最终重建完整的响应。
这是一个示例序列:
- 服务器开始发送响应。
- libcurl 接收第一个块并调用回调。
- 回调处理或存储块。
- libcurl 接收下一个块并再次调用回调。
- 此过程将继续,直到收到完整的回复。
ResponseCallback 函数的分步源代码解释
ResponseCallback 是 libcurl 接收数据时调用的函数。
函数声明
sudo apt-get install libcurl4-openssl-dev
登录后复制
登录后复制
登录后复制
登录后复制
-
void *contents:这是指向从服务器接收的数据的指针。 libcurl 提供了这个缓冲区并用它下载的数据填充它。
-
size_t size 和 size_t nmemb:这些表示每个内存块的大小(size)和块的数量(nmemb)。 size * nmemb 共同给出了该块中接收到的数据的总大小。
-
void *userp:这是通过curl_easy_setopt(curl, CURLOPT_WRITEDATA, ...)传递给回调函数的用户定义指针。在此示例中,它是指向 struct Memory 对象的指针,该对象存储完整的响应。
计算总数据大小
通过将一个块的大小 (size) 乘以块的数量 (nmemb) 来计算当前接收到的数据块的总大小。
例如,如果服务器发送 8 个块,每个块 256 字节,totalSize 将为 8 * 256 = 2048 字节。
访问用户数据(struct Memory)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
// Struct to hold response data
struct Memory {
char *response;
size_t size;
};
// Callback function to handle the data received from the API
static size_t ResponseCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t totalSize = size * nmemb;
struct Memory *mem = (struct Memory *)userp;
printf(". %zu %zu\n", size, nmemb);
char *ptr = realloc(mem->response, mem->size + totalSize + 1);
if (ptr == NULL) {
printf("Not enough memory to allocate buffer.\n");
return 0;
}
mem->response = ptr;
memcpy(&(mem->response[mem->size]), contents, totalSize);
mem->size += totalSize;
mem->response[mem->size] = '<pre class="brush:php;toolbar:false">gcc get.c -o get -lcurl
登录后复制
登录后复制
';
return totalSize;
}
int main() {
CURL *curl;
CURLcode res;
struct Memory chunk;
chunk.response = malloc(1); // Initialize memory
chunk.size = 0; // No data yet
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
// Set URL of the API endpoint
char access_token[] = "your-access-token";
char slug[] = "home";
char version[]= "draft";
char url[256];
snprintf(url, sizeof(url), "https://api.storyblok.com/v2/cdn/stories/%s?version=%s&token=%s", slug, version, access_token);
// Print the URL
printf("URL: %s\n", url);
// initializing libcurl
// setting the URL
curl_easy_setopt(curl, CURLOPT_URL, url );
// Follow redirect
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// Set callback function to handle response data
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ResponseCallback);
// Pass the Memory struct to the callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
// Perform the HTTP GET request
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
printf("Response data size: %zu\n", chunk.size);
//printf("Response data: \n%s\n", chunk.response);
}
// Cleanup
curl_easy_cleanup(curl);
}
// Free allocated memory
free(chunk.response);
curl_global_cleanup();
return 0;
}
userp 指针被转换为 struct Memory *。该结构体已在主程序中前面传递,用于累积接收到的数据。
结构体 Memory 定义为:
-
响应:动态分配的字符串,用于存储下载的数据。
-
size:响应字符串的当前大小。
重新分配内存
static size_t ResponseCallback(void *contents, size_t size, size_t nmemb, void *userp)
登录后复制
调整响应缓冲区的大小以容纳新的数据块:
-
mem->size:缓冲区的当前大小。
-
TotalSize:新块的大小。
-
1: 空终止符 ( ) 的空格使其成为有效的 C 字符串。
-
realloc:为响应缓冲区动态重新分配内存。
如果分配失败,realloc返回NULL,旧内存仍然有效。
处理内存分配错误
size_t totalSize = size * nmemb;
登录后复制
如果内存分配失败(因此 ptr 为 NULL),则打印错误消息并返回 0。返回 0 表示 libcurl 中止传输。
更新缓冲区
struct Memory *mem = (struct Memory *)userp;
登录后复制
-
mem->response = ptr:将新分配的内存分配回响应指针。
-
memcpy:将新的数据块从内容复制到缓冲区中:
-
&(mem->response[mem->size]):缓冲区中应附加新数据的位置(当前数据的末尾)。
-
content: 从服务器接收到的数据。
-
TotalSize:要复制的数据的大小。
更新总大小
struct Memory {
char *response;
size_t size;
};
登录后复制
增加响应缓冲区的大小以反映附加新块后的新总大小。
Null-终止响应字符串
sudo apt-get install libcurl4-openssl-dev
登录后复制
登录后复制
登录后复制
登录后复制
在响应缓冲区末尾添加一个空终止符,使其成为有效的 C 字符串。
这确保了响应可以被安全地视为常规的空终止字符串。
返回总大小
返回已处理的字节数(totalSize)。
这向 libcurl 发出信号,表明数据块已成功处理。
何时为 API 选择 C
在以下情况下使用 C 来使用 API:
- 性能很重要:C 是速度关键型应用的理想选择。
- 系统集成:您需要将网络请求与硬件操作结合起来(例如,为 PoS 系统获取数据)。
- 嵌入式系统:资源受限的设备受益于 C 语言的效率。
-
好奇心和探索:有时,您使用 C 只是因为您喜欢编程,并且想通过探索较低级语言来完成通常为高级语言保留的任务来挑战自己。这是加深您对事物底层工作原理的理解的好方法!
结论
在当今的高级编程世界中,使用 C 语言的 API 似乎不合常规,但对于需要性能、控制以及与系统级操作集成的场景来说,它是一个强大的工具。通过使用 libcurl 等库,开发人员可以轻松地将 HTTP 请求集成到 C 应用程序中,从而弥合现代 API 和传统系统级编程之间的差距。
有了这些知识,您就可以构建与 API 无缝交互的 C 应用程序,证明 C 即使在现代开发工作流程中仍然具有相关性。
以上是使用 C 语言 API:现代开发人员的实用指南的详细内容。更多信息请关注PHP中文网其他相关文章!