C++ 开发 Web 服务框架 - 基础知识:C++11 与 Boost Asio
一、概述
项目介绍
服务器开发中 Web 服务是一个基本的代码单元,将服务端的请求和响应部分的逻辑抽象出来形成框架,能够做到最高级别的框架级代码复用。本次项目将综合使用 C++11 及 Boost 中的 Asio 实现 HTTP 和 HTTPS 的服务器框架。
项目涉及的知识点
C++基本知识
面向对象
模板
命名空间
常用 IO 库
C++11 相关
lambda expression
std::shared_ptr
std::make_shared
std::unordered_map
std::regex
std::smatch
std::regex_match
std::function
std::thread
Boost Asio 相关
boost::asio::io_service
boost::asio::ip::tcp::socket
boost::asio::ip::tcp::v4()
boost::asio::ip::tcp::endpoint
boost::asio::ip::tcp::acceptor
boost::asio::streambuf
boost::asio::async_read
boost::asio::async_read_until
boost::asio::async_write
boost::asio::transfer_exactly
boost::asio::ssl::stream
boost::asio::ssl::stream_base::server
boost::asio::ssl::context
boost::asio::ssl::context::sslv23
boost::asio::ssl::context::pem
boost::system::error_code
编译环境提示
在 g++ 4.9 之前,regex 库并不支持 ECMAScript 的正则语法,换句话说,在 g++4.9 之前,g++ 对 C++11 标准库的支持并不完善,为保证本次项目的顺利进行,请确保将 g++ 版本升级至 4.9 以上。
// 下面的这段代码可以测试你的编译器对正则表达式的支持情况
#include <iostream>
#include <regex>
int main()
{
std::regex r1("S");
printf("S works.\n");
std::regex r2(".");
printf(". works.\n");
std::regex r3(".+");
printf(".+ works.\n");
std::regex r4("[0-9]");
printf("[0-9] works.\n");
return 0;
}
如果你的运行结果遇到了下图所示的错误,说明你确实需要升级你的 g++ 了:
使用 g++ -v 可以查看到当前编译器版本:
如果你最后一行中的 gcc version 显示的是 4.8.x,那么你需要手动将编译器版本升级至 4.9 以上,方法如下:
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install gcc-4.9 g++-4.9
sudo updatedb
sudo ldconfig
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 48 \
--slave /usr/bin/g++ g++ /usr/bin/g++-4.8 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-4.8 \
--slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-4.8 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-4.8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 49 \
--slave /usr/bin/g++ g++ /usr/bin/g++-4.9 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-4.9 \
--slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-4.9 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-4.9
此外,本次项目依赖了 Boost 和 OpenSSL 这两个库,不过好在实验楼的环境已经提供了这两个非常基本的库,你不需要再操心他们的安装了。
项目代码
本项目所有代码
可使用wget下载,也可以直接复制地址到浏览器下载
希望同学们能跟着一步一步实现
wget http://labfile.oss.aliyuncs.com/courses/568/web_server.zip
一、C++ 基础
面向对象和模板是 C++进阶知识的基础,这里不做过多介绍,本次项目我们将开发一个 Web 框架,我们在这里先回顾一下命名空间、和 sstream 字符串 IO 流的相关知识。如果对这部分比较熟悉,可以直接跳过本小节。
命名空间
在开发库时,库通常会有定义大量的全局名称,这时候当我们使用的库越来越多时,就不可避免的发生名称冲突的情况,这也就是我们常说的命名空间污染。
在命名空间诞生以前,通常使用的办法就是把一个函数、类、甚至变量名等名字取得足够长,在每一个名字的前面都增加相应的前缀,例如,当我们只想要定义一个 port 的变量时候:
// 原本的样子
int port;
// 实际的样子
int shiyanlou_web_server_port;
命名空间的定义非常简单,通过关键字 namespace 加上命名空间的名字,再使用花括号包裹需要的定义和声明即可完成相关的定义,例如:
namespace shiyanlou_web_server {
int port = 0;
}
这时,这个 port 就被限制在了命名空间 shiyanlou_web_server 当中,如果不通过命名空间的指定,就不会被访问到。
参考下面的例子:
//
// main.cpp
//
#include <iostream>
#include "web.hpp"
#include "web2.hpp"
int main() {
std::cout << "hello world!" << std::endl;
std::cout << "shiyanlou_web_server, port=" << shiyanlou_web_server::port << std::endl;
std::cout << "shiyanlou_web2_server, port=" << shiyanlou_web2_server::port << std::endl;
return 0;
}
//
// web.hpp
//
namespace shiyanlou_web_server{
int port = 0;
}
//
// web2.hpp
//
namespace shiyanlou_web2_server{
int port = 2;
}
最后的输出结果为:
hello world!
shiyanlou_web_server, port=0
shiyanlou_web2_server, port=2
常用 IO 库
我们常说的 C++ IO 库一般指 iostream, fstream, sstream。
iostream 包含了 istream(从流读)/ostream(从流写)/iostream(读写流)
fstream 包含了 ifstream(从文件读)/ofstream(condition 文件写)/fstream(读写文件)
sstream 包含了 istringstream(从 string 读)/ostringstream(向 string 写)/stringstream(读写 string)
其实标准库还有宽字符版本,但我们这里不讨论,有兴趣的话可以参考参考链接。
iostream 和 fstream 是两个比较常用的IO 库,我们这里不再回顾,这里简单回顾一下 sstream。
如果你熟悉 C 语言,就知道将 int 转换为 string 类型其实是一件很麻烦的事情,虽然标准库中提供了 itoa() 这种函数,但是依然需要对转换后的 C 风格字符串(char *)通过 std::string 的构造函数构造为 std::string。
如果使用流操作,那么这将变得异常的简单:
#include <string>
#include <sstream>
#include <iostream>
int main() {
// std::stringstream 支持读写
std::stringstream stream;
std::string result;
int number = 12345;
stream << number; // 将 number 输入到 stream
stream >> results; // 从 stream 读取到 result
std::cout < result << std::endl; // 将输出为字符串"12345"
}
如果希望让sstream 和 C 风格的字符串打交道,同样也可以:
#include <sstream>
#include <iostream>
int main()
{
std::stringstream stream;
char result[6];
stream << 12345;
stream >> result;
std::cout << result << std::endl;
}
需要注意的一点就是,在进行多次IO 操作时,如果希望结果彼此不影响,需要对 stream 对象进行一次 clear() 操作:
stream.clear()
二、C++11 相关
C++11 几乎重新定义了 C++ 的一切,C++11 的出现伴随着大量的有用的新特性和标准库,这些特性和标准使得 C++ 变得更加现代,甚至在编码范式上都与传统 C++ 有着本质上的差异,本节我们将回顾一下这些特性:
lambda expression
std::shared_ptr
std::make_shared
std::unordered_map
std::regex
std::smatch
std::regex_match
std::function
std::thread
如果对这些特性比较熟悉,可以直接跳过本节。
lambda 表达式
Lambda 表达式是 C++11中最重要的新特性之一,而 Lambda 表达式,实际上就是提供了一个类似匿名函数的特性,而匿名函数则是在需
来源: 实验楼
链接: https://www.shiyanlou.com/courses/568
本课程内容,由作者授权实验楼发布,未经允许,禁止转载、下载及非法传播
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。