如今,使用 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中文網其他相關文章!