1.Http协议介绍
HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的应用层协议,用于在客户端(如浏览器)和服务器之间传输超文本(如网页)数据。它是万维网(WWW)数据通信的基础,设计简单灵活,支持多种数据格式的传输。
1.HTTP 核心特点
-
无状态协议
默认不记录之前的请求或会话信息(后续可通过Cookie
、Session
等技术实现状态管理)。 -
基于请求-响应模型
客户端发送请求(Request
),服务器返回响应(Response
)。 -
支持多种方法
如GET
(获取资源)、POST
(提交数据)、PUT
(更新资源)、DELETE
(删除资源)等。 -
可扩展性强
通过请求头/响应头(Headers)传递额外信息,支持缓存、压缩、身份验证等功能。
2.HTTP 工作原理
-
建立连接
客户端通过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 <html>...</html>
-
-
关闭连接
(非持久连接时断开TCP连接)。
3.HTTP 版本演进
版本 | 特性 |
---|---|
HTTP/1.0 | 每次请求需重新建立连接,效率低。 |
HTTP/1.1 | 默认持久连接、管道化请求、支持分块传输(Transfer-Encoding: chunked )。 |
HTTP/2 | 二进制分帧、多路复用、头部压缩、服务器推送,大幅提升性能。 |
HTTP/3 | 基于QUIC协议(UDP),解决队头阻塞,提升弱网环境下的传输效率。 |
4.常见状态码
-
200 OK
:请求成功。 -
301 Moved Permanently
:永久重定向。 -
400 Bad Request
-
404 Not Found
:资源不存在。 -
500 Internal Server Error
:服务器内部错误。
HTTP 与 HTTPS 的区别
-
HTTPS = HTTP + SSL/TLS加密,通过端口443传输,保证数据安全性(防窃听、篡改)。
-
HTTPS需配置数字证书(如Let's Encrypt签发)。
2. curl使用
curl
是一个非常强大的命令行工具,用于在不同类型的服务器之间传输数据,支持多种协议(如 HTTP、HTTPS、FTP 等)。以下是一些常见的 curl
使用方法:
1.基本用法
-
发送 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。
2.高级用法
-
设置请求
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 响应头。
3.具体使用
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
需与服务端路由匹配。
-
4.libcurl
sudo apt update
sudo apt install libcurl4-openssl-dev
1. 初始化和清理
在使用 libcurl
之前,需要初始化一个 CURL
对象,并在使用完成后进行清理。
#include <stdio.h>
#include <curl/curl.h>
int main() {
CURL *curl;
CURLcode res;
// 初始化CURL对象
curl = curl_easy_init();
if(curl) {
// 设置选项和执行请求
// ...
// 清理CURL对象
curl_easy_cleanup(curl);
}
return 0;
}
2. 设置选项
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);
3. 获取响应信息
可以通过设置选项来获取响应信息,例如响应头和响应体。
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
中。
4. 执行请求
使用 curl_easy_perform
函数执行请求。该函数会阻塞,直到请求完成。
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
5. 示例代码
#include <stdio.h>
#include <curl/curl.h>
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;
}
6.编译和运行
gcc -o example example.c -lcurl
在C++中使用libcurl
的基本思路与C语言类似,但可以通过C++的特性(如智能指针、类封装等)来提高代码的可读性和安全性。以下是一个C++版本的示例,展示如何使用libcurl
进行HTTP请求。
1. 包含头文件
在C++中,libcurl
的头文件和C语言中一样,可以直接包含<curl/curl.h>
。
2. 初始化和清理
使用libcurl
时,仍然需要初始化CURL
对象,并在使用完成后进行清理。在C++中,可以通过RAII(Resource Acquisition Is Initialization)机制来管理资源,例如使用智能指针。
3. 设置选项和执行请求
设置选项和执行请求的逻辑与C语言相同,但可以利用C++的特性来简化代码。
4. 示例代码
以下是一个完整的C++示例,展示如何使用libcurl
发起一个HTTP GET请求并打印响应体。
#include <iostream>
#include <curl/curl.h>
// 回调函数,用于处理响应体
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
std::string response(static_cast<char*>(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
3. HTTP 请求
你的问题涉及到 HTTP 请求的两种常见方式:路径参数(Path Parameters)和查询参数(Query Parameters)。这两种方式都可以用来传递数据,但它们在语义和用途上有所不同。
1. 查询参数(Query Parameters)
查询参数是附加在 URL 路径后面的键值对,通常用于传递额外的信息。它们以 ?
开始,多个参数用 &
分隔。查询参数的主要用途包括:
-
过滤和排序:在 API 请求中,查询参数常用于指定过滤条件或排序方式。
-
分页:指定页码和每页显示的条数。
-
搜索:传递搜索关键词。
-
其他动态数据:传递动态生成的数据,如时间戳、随机值等。
示例
假设你有一个 API,用于获取用户列表,并支持分页和排序:
GET /users?page=1&limit=10&sort=asc
这里:
-
page=1
表示请求第一页。 -
limit=10
表示每页显示 10 条记录。 -
sort=asc
表示按升序排序。
2. 路径参数(Path Parameters)
路径参数是嵌入在 URL 路径中的变量部分,通常用于指定资源的唯一标识符。路径参数的主要用途包括:
-
资源标识:指定要操作的具体资源。
-
层次结构:表示资源的层次结构。
示例
假设你有一个 API,用于获取特定用户的详细信息:
GET /users/12345
这里:
-
12345
是用户的唯一标识符。
3. 你的场景
在你的场景中,你有一个下载接口,客户端需要指定要下载的文件块的 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/<string>")
.methods("GET"_method)([](const crow::request &req, std::string chunk_id) {
return dfs::DownloadManager::downloadById(chunk_id);
});
4. 选择哪种方式更好?
使用查询参数的优点
-
灵活性:查询参数可以传递多个参数,适合复杂的请求。
-
语义清晰:对于动态数据(如分页、排序、搜索等),查询参数更符合语义。
使用路径参数的优点
-
简洁性:路径参数使 URL 更简洁,适合传递资源的唯一标识符。
-
RESTful:路径参数更符合 RESTful API 的设计原则,使 URL 更具可读性和可维护性。