HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的应用层协议,用于在客户端(如浏览器)和服务器之间传输超文本(如网页)数据。它是万维网(WWW)数据通信的基础,设计简单灵活,支持多种数据格式的传输。
无状态协议
默认不记录之前的请求或会话信息(后续可通过Cookie
、Session
等技术实现状态管理)。
基于请求-响应模型
客户端发送请求(Request
),服务器返回响应(Response
)。
支持多种方法
如 GET
(获取资源)、POST
(提交数据)、PUT
(更新资源)、DELETE
(删除资源)等。
可扩展性强
通过请求头/响应头(Headers)传递额外信息,支持缓存、压缩、身份验证等功能。
建立连接
客户端通过TCP/IP(默认端口80)与服务器连接(HTTP/1.1默认持久连接,HTTP/2支持多路复用)。
发送请求
请求报文包括:
请求行:方法(如GET
)、URL(如/index.html
)、协议版本(如HTTP/1.1
)。
请求头:Host
、User-Agent
、Accept
等元信息。
请求体(可选):如POST
提交的表单数据。
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
服务器处理并返回响应
响应报文包括:
状态行:状态码(如200 OK
)、协议版本。
响应头:Content-Type
、Content-Length
等。
响应体:实际数据(如HTML、JSON)。
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
...
关闭连接
(非持久连接时断开TCP连接)。
版本 | 特性 |
---|---|
HTTP/1.0 | 每次请求需重新建立连接,效率低。 |
HTTP/1.1 | 默认持久连接、管道化请求、支持分块传输(Transfer-Encoding: chunked )。 |
HTTP/2 | 二进制分帧、多路复用、头部压缩、服务器推送,大幅提升性能。 |
HTTP/3 | 基于QUIC协议(UDP),解决队头阻塞,提升弱网环境下的传输效率。 |
200 OK
:请求成功。
301 Moved Permanently
:永久重定向。
400 Bad Request
404 Not Found
:资源不存在。
500 Internal Server Error
:服务器内部错误。
HTTPS = HTTP + SSL/TLS加密,通过端口443传输,保证数据安全性(防窃听、篡改)。
HTTPS需配置数字证书(如Let's Encrypt签发)。
curl
是一个非常强大的命令行工具,用于在不同类型的服务器之间传输数据,支持多种协议(如 HTTP、HTTPS、FTP 等)。以下是一些常见的 curl
使用方法:
发送 GET 请求
curl http://example.com
这是最基本的用法,用于从指定的 URL 获取数据。
发送 POST 请求
curl -X POST http://example.com -d "param1=value1¶m2=value2"
-X POST
指定请求方法为 POST,-d
用于指定要发送的数据。
发送 PUT 请求
curl -X PUT http://example.com -d "param1=value1¶m2=value2"
-X PUT
指定请求方法为 PUT,同样使用 -d
发送数据。
发送 DELETE 请
curl -X DELETE http://example.com
-X DELETE
指定请求方法为 DELETE。
设置请求
curl -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://example.com
-H
参数用于添加自定义的 HTTP 请求头。
上传文件
bashcurl -X POST -F "file=@/path/to/file" http://example.com/upload
-F
参数用于上传文件,@
后面跟文件路径。
下载文件
curl -O http://example.com/file.zip
-O
参数会将文件保存为原文件名。
curl -o custom_filename.zip http://example.com/file.zip
-o
参数可以指定保存的文件名。
查看响应头
curl -I http://example.com
-I
参数只获取 HTTP 响应头。
curl -X POST -H "Filename: example.txt" --data-binary "@/home/book/Desktop/chat/data/local/example.txt" http://localhost:8080/uploadtp://localhost:8080/upload
-X POST
指定 HTTP 请求方法为 POST
(通常用于上传操作)。
-H "Filename: example.txt"
添加一个自定义请求头,键为 Filename
,值为 example.txt
。
作用:服务器可能通过此头部获取客户端指定的文件名,而非从文件数据中解析。
--data-binary @/path/to/file
--data-binary
表示发送原始二进制数据(不会进行编码/转义)。
@
符号告诉 curl 从指定路径读取文件内容(这里是 /home/book/Desktop/chat/data/local/example.txt
)。
http://localhost:8080/upload
curl -o /home/book/Desktop/chat/data/download/example.txt http://localhost:8081/download/example.txt
curl -o /home/book/Desktop/chat/data/download/example.txt http://localhost:8081/download/6ab618eb-cee1-4809-bd42-22c4d21ca535
-o /path/to/save/file
将服务器返回的数据保存到指定路径(而不是输出到终端)。
http://localhost:8081/download/example.txt
请求的URL,假设服务端在 8081
端口提供了文件下载接口。
路径 /download/example.txt
需与服务端路由匹配。
sudo apt update
sudo apt install libcurl4-openssl-dev
在使用 libcurl
之前,需要初始化一个 CURL
对象,并在使用完成后进行清理。
#include
#include
int main() {
CURL *curl;
CURLcode res;
// 初始化CURL对象
curl = curl_easy_init();
if(curl) {
// 设置选项和执行请求
// ...
// 清理CURL对象
curl_easy_cleanup(curl);
}
return 0;
}
libcurl
提供了大量的选项,用于配置请求的各种参数。这些选项通过 curl_easy_setopt
函数设置。
设置 URL :CURLOPT_URL
,指定请求的 URL。
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
设置超时时间 :CURLOPT_TIMEOUT
,单位为秒
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 设置超时时间为10秒
启用 SSL 验证 :CURLOPT_SSL_VERIFYPEER
和 CURLOPT_SSL_VERIFYHOST
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 禁用SSL证书验证
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 禁用主机名验证
设置 HTTP 请求头 :CURLOPT_HTTPHEADER
。
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Authorization: Bearer YOUR_TOKEN");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_slist_free_all(header);
可以通过设置选项来获取响应信息,例如响应头和响应体。
size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
printf("Header: %s", buffer);
return size * nitems;
}
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
获取响应体
size_t write_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
printf("Body: %s", buffer);
return size * nitems;
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
这行代码设置了自定义的回调函数,用于处理响应体数据。
CURLOPT_WRITEFUNCTION
:这是libcurl
的一个选项,用于指定一个回调函数,当接收到响应体数据时,libcurl
会调用这个函数。
write_callback
:这是一个用户定义的函数,libcurl
会在接收到响应体数据时调用它。这个函数的原型通常如下:
size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp);
contents
:指向接收到的数据的指针。
size
:每个数据块的大小。
nmemb
:数据块的数量。
userp
:用户提供的数据指针,通过CURLOPT_WRITEDATA
设置。
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
这行代码设置了传递给回调函数的用户数据。
CURLOPT_WRITEDATA
:这是libcurl
的一个选项,用于指定一个用户数据指针,这个指针将被传递给CURLOPT_WRITEFUNCTION
指定的回调函数。
&response
:这是用户提供的数据的地址。在这个例子中,response
可能是一个变量,用于存储响应体数据。
这行代码的作用是将response
的地址传递给write_callback
函数,这样write_callback
函数就可以将接收到的数据存储到response
中。
使用 curl_easy_perform
函数执行请求。该函数会阻塞,直到请求完成。
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
#include
#include
size_t write_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
printf("Body: %s", buffer);
return size * nitems;
}
int main() {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
return 0;
}
gcc -o example example.c -lcurl
在C++中使用libcurl
的基本思路与C语言类似,但可以通过C++的特性(如智能指针、类封装等)来提高代码的可读性和安全性。以下是一个C++版本的示例,展示如何使用libcurl
进行HTTP请求。
1. 包含头文件
在C++中,libcurl
的头文件和C语言中一样,可以直接包含
。
2. 初始化和清理
使用libcurl
时,仍然需要初始化CURL
对象,并在使用完成后进行清理。在C++中,可以通过RAII(Resource Acquisition Is Initialization)机制来管理资源,例如使用智能指针。
3. 设置选项和执行请求
设置选项和执行请求的逻辑与C语言相同,但可以利用C++的特性来简化代码。
4. 示例代码
以下是一个完整的C++示例,展示如何使用libcurl
发起一个HTTP GET请求并打印响应体。
#include
#include
// 回调函数,用于处理响应体
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
std::string response(static_cast(contents), totalSize);
std::cout << "Response Body: " << response << std::endl;
return totalSize;
}
int main() {
CURL* curl;
CURLcode res;
// 初始化CURL对象
curl = curl_easy_init();
if (curl) {
// 设置URL
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
// 设置回调函数,用于处理响应体
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
// 执行请求
res = curl_easy_perform(curl);
// 检查错误
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
}
// 清理CURL对象
curl_easy_cleanup(curl);
}
return 0;
}
g++ -o example example.cpp -lcurl
你的问题涉及到 HTTP 请求的两种常见方式:路径参数(Path Parameters)和查询参数(Query Parameters)。这两种方式都可以用来传递数据,但它们在语义和用途上有所不同。
查询参数是附加在 URL 路径后面的键值对,通常用于传递额外的信息。它们以 ?
开始,多个参数用 &
分隔。查询参数的主要用途包括:
过滤和排序:在 API 请求中,查询参数常用于指定过滤条件或排序方式。
分页:指定页码和每页显示的条数。
搜索:传递搜索关键词。
其他动态数据:传递动态生成的数据,如时间戳、随机值等。
假设你有一个 API,用于获取用户列表,并支持分页和排序:
GET /users?page=1&limit=10&sort=asc
这里:
page=1
表示请求第一页。
limit=10
表示每页显示 10 条记录。
sort=asc
表示按升序排序。
路径参数是嵌入在 URL 路径中的变量部分,通常用于指定资源的唯一标识符。路径参数的主要用途包括:
资源标识:指定要操作的具体资源。
层次结构:表示资源的层次结构。
假设你有一个 API,用于获取特定用户的详细信息:
GET /users/12345
这里:
12345
是用户的唯一标识符。
在你的场景中,你有一个下载接口,客户端需要指定要下载的文件块的 chunk_id
。你可以选择使用查询参数或路径参数来传递 chunk_id
。
std::string url = "/internal_download?chunk_id=" + chunk.chunk_id;
auto res = client.Get(url.c_str());
服务器端:
CROW_ROUTE(app, "/internal_download")
.methods("GET"_method)([](const crow::request &req) {
std::string chunk_id = req.url_params.get("chunk_id");
return dfs::DownloadManager::downloadById(chunk_id);
});
std::string url = "/internal_download/" + chunk.chunk_id;
auto res = client.Get(url.c_str());
服务器端:
CROW_ROUTE(app, "/internal_download/")
.methods("GET"_method)([](const crow::request &req, std::string chunk_id) {
return dfs::DownloadManager::downloadById(chunk_id);
});
灵活性:查询参数可以传递多个参数,适合复杂的请求。
语义清晰:对于动态数据(如分页、排序、搜索等),查询参数更符合语义。
简洁性:路径参数使 URL 更简洁,适合传递资源的唯一标识符。
RESTful:路径参数更符合 RESTful API 的设计原则,使 URL 更具可读性和可维护性。