前言
最近在做的一个项目需要使用到 HTTP 协议,在网上查了很久,也结合了之前学长做的项目,发现 cpp-http 库使用挺多的,所以就边学边做笔记,顺便分享出来(我觉得知识之所以叫知识,就是因为它能被记录并能被传播,为人所知,为人所识)。
我准备把这个学习记录做成一个系列文章,这个系列里面,我会介绍从 HTTP 协议诞生到目前的一些大概情况,然后会简单介绍一下目前 C/C++ 下面用的比较广泛的 HTTP 库,之后就是讲解 cpp-http 库的简单使用、 cpp-http 库的实现,最后如果时间允许的话可以自己手码一个 C 语言的 http 用来作总结。
反正 flag 在这里先立下了,这篇文章是这个系列的第一篇,但是并不是按顺序来的第一篇,这里讲的是 cpp-http 库的简单使用,按顺序来看的话应该是系列里面的第三篇或者第二篇。如果大家目前对 HTTP 协议基础知识有需求的话还请移步其他博主的文章,之后我也会陆续把各个模块补上(下一篇可能是 cpp-http 库的实现部分),敬请大家期待。
另:本人目前是大学生,新人一枚,文章之中难免会有纰漏,还请大家多多包涵,不吝赐教。
cpp-http 库简介
-
cpp-http 库是什么? cpp-http 由是一位国外程序员 yhirose 在 github 上面开源的一个 C++ 项目,同时这个项目得到了世界各地程序员的支持,目前包括原作者在内共有 104 位贡献者,拥有 5.7k Star 和 1.2k Fork ,是目前使用较为广泛的一个 C++ http库之一。 -
cpp-http 项目地址 https://github.com/yhirose/cpp-httplib 如果网络不好不能访问 github 的话,这里是国内一位程序员搬运到 gitee 上面的项目,内容都是一样的: https://gitee.com/zhangkt1995/cpp-httplib?_from=gitee_search -
关于它的简介,这里引用原作者的话:
A C++11 single-file header-only cross platform HTTP/HTTPS library.
It’s extremely easy to setup. Just include the httplib.h file in your code!
NOTE: This is a multi-threaded ‘blocking’ HTTP library. If you are looking for a ‘non-blocking’ library, this is not the one that you want.
翻译过来就是:
一个只有头文件的跨平台 HTTP/HTTPS 库。
简单易用,只需要包含头文件 httplib,h 即可。
注意:这个库是一个多线程阻塞式 Http 库,如果您需要的是一个非阻塞式的库,这个库并不适合您。
cpp-http 库使用介绍
网上大多数教程都只介绍了使用 cpp-http 库的 GET 请求使用,没有 POST 请求的(其实两者使用一模一样),在这里我也加上了 POST 请求的部分。
http 客户端搭建步骤
- 组织http协议格式的请求数据
- 搭建tcp客户端
- 发送组织好的http请求数据
- 等待服务端响应,接收响应数据
- 对响应数据的解析
http 服务端搭建步骤
- 搭建tcp服务端
- 等待接收客户端发送的数据
- 按照 http 协议格式,对数据进行解析(格式按照: 请求方法 URL 协议版本\r\n 头部\r\n 正文)
- 根据请求的资源路径以及查询字符串以及正文,进行业务处理
- 组织http协议格式的响应,返回给客户端(协议版本 状态码 描述\r\n 头部)
cpp-http 库示例
这里我们先用个 demo 来看看 cpp-http 库怎么使用:
首先先建立工程,目录如下:
? tree ../http -L 1
../http
├── client.cpp
├── httplib.h
└── server.cpp
0 directories, 5 files
其中 httplib.h 就是 cpp-http 库的所有内容了,就是这一个头文件;server.cpp 是我们自己的服务端程序, client.cpp 是我们自己的客户端程序。
服务端实现
- 代码。相关的解释都在注释里面
#include <iostream>
#include "httplib.h"
using Request = httplib::Request;
using Response = httplib::Response;
using Server = httplib::Server;
using Params = httplib::Params;
void get_go(const Request& req, Response& res)
{
res.set_content("<html><h1>I'm the king of the world!</h1></html>", "text/html");
std::cout << "Received a request of get [go]." << std::endl;
}
int main(int argc, char const *argv[])
{
Server srv;
srv.Get("/go", get_go);
srv.Get("/hi", [&](const Request& req, Response& res){
res.set_content("<html><h1>Hello world!</h1></html>", "text/html");
std::cout << "Received a request of get [hi]." << std::endl;
});
srv.Get("/link", [&](const Request& req, Response& res){
res.set_content("<html><h1 href=https://baike.baidu.com/item/%E8%A5%BF%E5%8D%8E%E5%A4%A7%E5%AD%A6%E6%9C%BA%E5%99%A8%E4%BA%BA%E8%B6%B3%E7%90%83%E5%8D%8F%E4%BC%9A/22274030>soccer robot</h1></html>", "text/html");
std::cout << "Received a request of get [link]." << std::endl;
});
srv.Post("/get_info", [&](const Request& req, Response& res) {
res.set_content("<html><h1>Go ahead!</h1></html>", "text/html");
std::cout << "Received a request of POST [get_info]." << std::endl;
});
srv.listen("0.0.0.0", 12345);
return 0;
}
这里大家可能有三个疑问:
Server::Get() 、Server::Post() 注册函数的实现机制,收到请到 GET 请求后是怎么执行对应函数的- 使用
Server::listen() 启动端口的实现机制 - lambda 表达式
其中前两点我会在后面解释,关于lambda表达式的部分可能之后会出相应的文章来介绍,在此之前还请各位移步其他关于 lambda 的文章进行学习。
- 编译
cpp-http 库编译时需要使用线程库 pthread 编译时使用 g++ 直接编译,使用 gcc 编译会报错(毕竟 gcc 是为 C 设计的)
编译时使用命令:
g++ ./server.cpp -lpthread -o sever.out
- 执行
执行服务端程序:
./server.out
- 验证服务端:
$ curl http://ip:port/source
最后一行 使用 curl http://0.0.0.0:12345/get_info 时没有返回,并且服务器端也没有接收到数据,说明 curl 直接发送 /get_info 并不能用于提交 POST 请求,怎么使用 curl 提交 POST 请求等我之后有时间了再看看。
? curl http://0.0.0.0:12345/hi
<html><h1>Hello world!</h1></html>%
? curl http://0.0.0.0:12345/link
<html><h1 href=https://baike.baidu.com/item/%E8%A5%BF%E5%8D%8E%E5%A4%A7%E5%AD%A6%E6%9C%BA%E5%99%A8%E4%BA%BA%E8%B6%B3%E7%90%83%E5%8D%8F%E4%BC%9A/22274030>soccer robot</h1></html>%
? curl http://0.0.0.0:12345/go
<html><h1>I'm the king of the world!</h1></html>%
? curl http://0.0.0.0:12345/get_info
通过在浏览器地址栏输入相应的地址来验证 http 服务端,直接在浏览器地址栏填入链接就好了: 例如我在浏览器输入如下地址
http://http://0.0.0.0:12345/hi
结果如图:
? ./server.out Received a request of get [link]. Received a request of get [link]. Received a request of get [hi]. Received a request of get [link]. Received a request of get [link]. Received a request of get [go]. Received a request of get [hi].
客户端实现
- 代码
#include <iostream>
#include <string>
#include "httplib.h"
using Client = httplib::Client;
using string = std::string;
int main(int argc, char const *argv[])
{
if (argc < 3) {
std::cout << "Please input server ip and port" << std::endl;
return 0;
}
string source;
Client cli(argv[1], atoi(argv[2]));
while (true) {
std::cout << "Please input the source you want"
<< "(Or use <CTRL + C> to exit):"
<< std::endl;
std::getline(std::cin, source, '\n');
std::shared_ptr<httplib::Response> res;
if (source == "/get_info") {
res = cli.Post(source.c_str());
} else {
res = cli.Get(source.c_str());
}
if (res) {
if (res->status == 200) {
std::cout << "Recved response from server: \n\r"
<< res->body
<< "\n\r" << std::endl;
} else{
std::cout << "Request error: " << res->status
<< "\n\r" << std::endl;
}
} else {
std::cout << "Request GET failed.\n\r" << std::endl;
}
sleep(1);
}
return 0;
}
- 编译
- 编译命令和 编译 server.cpp 是一样的,不过参数要改成
client.cpp :
g++ ./client.cpp -lpthread -o client.out
? tree -L 1 ../http
../http
├── client.cpp
├── client.out
├── httplib.h
├── server
├── server.cpp
└── server.out
0 directories, 6 files
- 执行
./server.out
? ./client.out 0.0.0.0 12345
Please input the source you want(Or use <CTRL + Z> to exit):
/go
Recved response from server:
<html><h1>I'm the king of the world!</h1></html>
Please input the source you want(Or use <CTRL + Z> to exit):
/link
Recved response from server:
<html><h1 href=https://baike.baidu.com/item/%E8%A5%BF%E5%8D%8E%E5%A4%A7%E5%AD%A6%E6%9C%BA%E5%99%A8%E4%BA%BA%E8%B6%B3%E7%90%83%E5%8D%8F%E4%BC%9A/22274030>soccer robot</h1></html>
Please input the source you want(Or use <CTRL + Z> to exit):
/hi
Recved response from server:
<html><h1>Hello world!</h1></html>
Please input the source you want(Or use <CTRL + Z> to exit):
- 程序执行结果:
参考资料
- 如何用chrome浏览器发送POST请求:https://www.hm1006.cn/archives/chromepost
- 【项目】cpp-httplib库的原理:https://blog.csdn.net/weixin_43939593/article/details/104263043
- 推荐一个比较好用的c++版本http协议库-cpp-httplib:https://blog.csdn.net/wuquan_1230/article/details/117690607
|