10. 如何发起http请求
大家好,我是程序喵。
前面我们设计了上报的协议,并且确定使用http协议做埋点的网络上报。
HTTP请求是基于TCP协议的,它涉及以下几个主要步骤:
- 解析域名,获取对应的IP地址和端口号。
- 建立TCP连接,确保客户端和服务器之间的通信。
- 获取套接字通道FD,用于在通道中传输数据。
- 向通道FD中写入HTTP相关信息,包括请求方法、目标路径、HTTP版本以及请求头字段等。
- 将要上传的请求体写入通道FD中,向服务器发送数据。
- 从通道FD接收服务端返回的数据,获取服务器的响应。
- 关闭TCP链接,结束通信。
值得注意,HTTP协议本身只是一个应用层协议,定义了一些应用层规则。上述步骤中的许多细节实际上并不属于HTTP的一部分,我将它们整理到了HTTP通信的代码流程中,方便更好地理解整个过程。
在C++中,有很多关于HTTP请求的第三方库可供选择,包括Beast、cpp-httplib、libcurl、libhv等。在这些库中,Boost中的Beast可能是使用难度最高、集成使用最复杂的。尽管如此,我仍然在这个项目中选择Beast作为HTTP请求的库,主要是因为Beast的HTTP请求是基于Asio库的封装。
在使用Beast的过程中,我们将更加深入了解Asio库的设计理念和使用方法。值得一提的是,Asio将成为C++标准库的一部分,因此选择Beast可以让我们提前熟悉这套Asio设计,相信这可以为我们未来的职业生涯增加一些优势。
如何使用Beast发起http请求?
直接看代码:
这段代码是一个使用Boost的Beast库发起HTTP请求的示例。整体和我上面介绍的HTTP流程类似,我来详细解释一下每个步骤:
首先,这里指定了要请求的主机名(host)和目标路径(target)。接下来,创建了一个io_context对象,该对象对所有的I/O操作都是必需的,可以理解为是IO操作的运行时环境。我们还创建了一个resolver对象和一个tcp_stream对象,用于执行I/O操作。
接着,使用resolver对象来解析host,获取对应的IP地址和端口号。然后,使用tcp_stream对象与服务器建立TCP连接。
接着,定义了一个http::request对象,表示一个HTTP POST请求。我们设置了请求的方法为POST,目标路径为target,HTTP版本为11。然后,这里设置了一些HTTP头字段,包括host、user_agent和content_type。我们将请求的主体(body)设置为测试的body{"hello":"world"},并调用prepare_payload方法,用于构造整个请求体。
接着,我们使用http::write函数将请求发送到远程host。然后使用beast::flat_buffer对象作为缓冲区来接收服务器的response。这里定义了一个http::response对象来存储接收到的HTTP响应。
然后,我们使用http::read函数从流中读取服务器的响应。我们将响应的主体转换为字符串,并打印到标准输出。我们还打印了完整的响应和响应状态码。
最后,我们使用beast::error_code对象来处理错误,并通过调用shutdown方法优雅地关闭socket连接。
当然,如果发生了异常,这里也会捕获并打印相关的错误信息。
相关代码:https://github.com/chengxumiaodaren/BuriedPoint/blob/main/tests/test_http.cc
到这里,大家应该已经知道了如何使用Beast发起http请求。
但,这样肯定不太好用,难道每次发起网络请求都要写这么一大堆代码吗?我们需要基于埋点项目需求,封装一套好用的http接口。
我再解释下:
首先我定义了一个名为HttpReporter的类,用于封装一个方便使用的HTTP接口。
在类的构造函数中,接收一个std::shared_ptr<spdlog::logger>类型的参数,用于记录日志,看过我前几节专栏的朋友应该会知道如何传入这个logger实例。
类中定义了一些公有成员函数,用于设置HTTP请求的各个参数。Host函数用于设置主机名,Topic函数用于设置具体路径,Port函数用于设置端口号,Body函数用于设置请求的主体。
这些函数都返回一个HttpReporter&类型的引用,允许我们使用链式调用来方便地设置参数。
最后,定义了一个Report函数,用于发起HTTP请求。该函数会使用之前设置的参数,使用Boost的Beast库发起HTTP请求,并返回一个bool类型的值,表示请求是否成功。
整个HttpReporter类的设计目的是为了封装和简化HTTP请求的过程,让它更加方便易用。
然后,我们再实现它:
接口都已经实现好之后,我们就可以方便的发起http请求。
这样简单一调用就OK了。
如何测试http的代码?
这里大家可以自己搭建一个server,也可以使用我在项目中使用Rust开发的简易server。
直接进入server目录(https://github.com/chengxumiaodaren/BuriedPoint/tree/main/server),执行cargo run命令即可。
无论你选择哪种方法,都需要先安装Rust开发环境。
到这里,本节内容已经介绍完毕。在下一节中,我将详细介绍项目中加解密功能的实现。下周再见!
本节相关代码见:
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/src/report/http_report.h
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/src/report/http_report.cc
- https://github.com/chengxumiaodaren/BuriedPoint/blob/main/tests/test_http.cc
- https://github.com/chengxumiaodaren/BuriedPoint/tree/main/server
