C 语言标准库本身不包含任何用于网络请求(如 HTTP)的函数。HttpWebRequest 是 C# (一个高级语言) 中 .NET 框架提供的一个非常方便的类。

在 C 语言中,我们需要使用操作系统提供的底层网络 API,或者使用第三方库来简化这个过程,对于 Linux/Unix-like 系统,最标准、最常用的库是 libcurl,对于 Windows,你可以使用 WinINet 或 WinHTTP,但跨平台和功能最强大的选择仍然是 libcurl。
这篇教程将主要围绕 libcurl 展开,因为它是在 C 语言中进行 HTTP 请求的事实标准。
什么是 libcurl?
libcurl 是一个免费、开源、客户端的 URL 传输库,它支持多种协议(HTTP, HTTPS, FTP, SMTP 等),它功能强大,稳定,并且被无数应用程序使用,你可以把它看作是 C 语言世界的“瑞士军刀”,专门用来处理各种网络连接和数据传输。
环境准备:安装 libcurl
在使用 libcurl 之前,你需要确保你的开发环境中已经安装了它和它的开发头文件。

在 Linux (Debian/Ubuntu) 上:
sudo apt-get update sudo apt-get install libcurl4-openssl-dev
在 Linux (Fedora/CentOS) 上:
sudo dnf install libcurl-devel
在 Windows 上:
- 下载: 从 libcurl 官网 下载预编译的二进制包(
curl-8.x.x-win64-mingw.zip)。 - 配置:
- 将
include文件夹中的头文件(如curl.h)添加到你的编译器的 include 路径。 - 将
lib文件夹中的库文件(如libcurl.lib)添加到你的链接器设置中。 - 将
bin文件夹中的curl.dll复制到你的可执行文件所在的目录,或者添加到系统的PATH环境变量中。
- 将
libcurl 的基本使用流程
使用 libcurl 的基本步骤非常清晰,类似于 C# 中 HttpWebRequest 的模式:
- 初始化: 调用
curl_easy_init()创建一个 easy handle(句柄),这个句柄会保存所有关于此次请求的配置信息。 - 设置选项: 使用
curl_easy_setopt()函数来配置你的请求,这是最核心的一步,你可以设置 URL、请求方法(GET/POST)、请求头、回调函数等。 - 执行: 调用
curl_easy_perform()来执行请求,这个函数是阻塞的,直到请求完成或失败。 - 清理: 调用
curl_easy_cleanup()来释放 easy handle 及其相关的所有资源。
核心代码示例
下面我们通过几个例子来展示如何使用 libcurl。
示例 1:发送一个简单的 GET 请求
这个例子会向 httpbin.org/get 发送一个 GET 请求,并将响应内容打印到标准输出。
#include <stdio.h>
#include <curl/curl.h>
// 这是一个回调函数,libcurl 会将接收到的数据块传递给这个函数
// size_t 是数据块的大小,nmemb 是数据块的数量
// 返回值必须是实际处理的数据大小
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
(void)userp; // 避免未使用参数的警告
// 计算总字节数
size_t realsize = size * nmemb;
// 将数据直接写入标准输出
fwrite(contents, 1, realsize, stdout);
return realsize;
}
int main(void) {
CURL *curl;
CURLcode res;
// 1. 初始化 libcurl
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
// 2. 设置 URL
curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/get");
// 设置一个回调函数,用于处理服务器返回的数据
// CURLOPT_WRITEFUNCTION 指定你的回调函数
// CURLOPT_WRITEDATA 指定传递给回调函数的额外参数(这里我们不需要,传NULL)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
// 3. 执行请求
printf("Sending GET request to https://httpbin.org/get\n");
res = curl_easy_perform(curl);
// 检查执行结果
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
// 4. 清理
curl_easy_cleanup(curl);
}
// 全局清理
curl_global_cleanup();
return 0;
}
编译和运行 (Linux):

gcc -o simple_get simple_get.c -lcurl ./simple_get
示例 2:发送一个 POST 请求(发送 JSON 数据)
这个例子演示了如何设置请求方法为 POST,并添加自定义的请求头(如 Content-Type: application/json)。
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
(void)userp;
size_t realsize = size * nmemb;
fwrite(contents, 1, realsize, stdout);
return realsize;
}
int main(void) {
CURL *curl;
CURLcode res;
const char *post_data = "{\"name\":\"John Doe\", \"email\":\"john.doe@example.com\"}";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/post");
// 设置为 POST 请求
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
// 设置 POST 数据大小,libcurl 可以自动计算,但显式设置是好的做法
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data));
// 设置自定义请求头
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 设置回调函数
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
printf("Sending POST request with JSON data...\n");
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
// 清理请求头
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
C# HttpWebRequest 与 C libcurl 功能对比
为了让你更好地理解,这里有一个功能对比表格:
| 功能 | C# HttpWebRequest |
C libcurl | C libcurl 实现方式 |
|---|---|---|---|
| 初始化 | HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); |
CURL *curl = curl_easy_init(); |
curl_easy_init() |
| 设置 URL | req.RequestUri = new Uri(url); |
curl_easy_setopt(curl, CURLOPT_URL, url); |
CURLOPT_URL |
| GET 请求 | (默认为 GET) | (默认为 GET) | 无需特殊设置 |
| POST 请求 | req.Method = "POST"; |
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); |
CURLOPT_POSTFIELDS |
| 设置请求头 | req.Headers.Add("Content-Type", "application/json"); |
struct curl_slist *headers = NULL;headers = curl_slist_append(headers, "Header-Name: Value");curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); |
curl_slist_append 和 CURLOPT_HTTPHEADER |
| 获取响应状态码 | int statusCode = (int)req.GetResponse().StatusCode; |
long http_code = 0;curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); |
CURLINFO_RESPONSE_CODE |
| 读取响应体 | using (var stream = response.GetResponseStream()){ using(var reader = new StreamReader(stream)) ... } |
size_t WriteCallback(void *data, size_t size, size_t nmemb, void *userp)fwrite(data, size, nmemb, stdout); |
CURLOPT_WRITEFUNCTION 回调 |
| 执行请求 | using (var response = (HttpWebResponse)req.GetResponse()) |
curl_easy_perform(curl); |
curl_easy_perform() |
| 清理资源 | using 语句自动处理 |
curl_easy_cleanup(curl);curl_global_cleanup(); |
curl_easy_cleanup() 和 curl_global_cleanup() |
高级功能和注意事项
- SSL/TLS (HTTPS): libcurl 默认支持 HTTPS,如果需要自定义证书或验证逻辑,可以使用
CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST等选项。 - 超时设置: 使用
CURLOPT_TIMEOUT(总超时) 和CURLOPT_CONNECTTIMEOUT(连接超时) 来防止请求无限期挂起。 - 错误处理:
curl_easy_perform()返回CURLcode,你需要检查它是否为CURLE_OK。curl_easy_strerror()可以将错误码转换为可读的字符串。 - 内存管理: 在示例中,我们直接将数据写入文件(
stdout),在实际应用中,你可能需要将响应数据保存在内存缓冲区中,这需要你在WriteCallback中动态分配内存,并在请求结束后由主程序释放,这会增加内存管理的复杂性。 - 多线程: libcurl 的
easy接口不是线程安全的,如果你想在多线程环境中使用,每个线程都必须有自己的CURL *句柄,或者,你可以使用curl_multi接口,它被设计用于在单个线程中管理多个并发连接。
虽然 C 语言没有像 C# 那样封装好的 HttpWebRequest,但通过使用 libcurl 库,你可以实现所有常见的 HTTP 请求功能,包括 GET、POST、自定义请求头、处理响应、支持 HTTPS 等,libcurl 的学习曲线比 HttpWebRequest 稍陡峭,因为它更底层,需要你手动处理回调函数和内存管理,但它提供了无与伦比的灵活性和强大的功能,是 C 语言网络编程的首选工具。
