Boost::Beast::Http服务器
命名空间和类型定义
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;
这些命名空间和类型定义简化了后续代码的书写。
命名空间 my_program_state
namespace my_program_state
{std::size_t request_count(){static std::size_t count = 0;return ++count;}std::time_t now(){return std::time(0);}
}
这个命名空间包含两个函数:
request_count()
:返回请求计数,每次调用时递增。now()
:返回当前时间。
类 http_connection
这个类处理每个HTTP连接,继承自 std::enable_shared_from_this
以便在异步操作中安全地创建 shared_ptr
。
构造函数和 start
方法
http_connection(tcp::socket socket): m_socket(std::move(socket))
{
}void start()
{read_request();check_deadline();
}
构造函数接受一个TCP套接字。start
方法启动读取请求和超时检测。
私有成员变量
tcp::socket m_socket;
beast::flat_buffer m_buffer{ 8192 };
http::request<http::dynamic_body> m_request;
http::response<http::dynamic_body> m_response;
net::steady_timer m_deadline{m_socket.get_executor(), std::chrono::seconds(60)};
这些成员变量包括套接字、缓冲区、请求和响应对象,以及一个超时定时器。
read_request
方法
void read_request()
{auto self = shared_from_this();http::async_read(m_socket, m_buffer, m_request, self {if (!ec){self->process_request();}});
}
这个方法异步读取HTTP请求,并在读取完成后调用 process_request
方法。
check_deadline
方法
void check_deadline()
{auto self = shared_from_this();m_deadline.async_wait(self {if (!ec){self->m_socket.close(ec);}});
}
这个方法设置一个超时定时器,如果超时则关闭套接字。
process_request
方法
void process_request()
{m_response.version(m_request.version());m_response.keep_alive(false);switch (m_request.method()){case http::verb::get:m_response.result(http::status::ok);m_response.set(http::field::server, "Beast");create_response();break;case http::verb::post:m_response.result(http::status::ok);m_response.set(http::field::server, "Beast");create_post_response();break;default:m_response.result(http::status::bad_request);m_response.set(http::field::content_type, "text/plain");beast::ostream(m_response.body()) << "Invalid request-method" << std::string(m_request.method_string()) << "`" << std::endl;break;}write_response();
}
这个方法处理HTTP请求,根据请求方法生成响应。
create_response
方法
void create_response()
{if (m_request.target() == "/count"){m_response.set(http::field::content_type, "text/html");beast::ostream(m_response.body())<< "<html>\n"<< "<head><title>Request count</title></head>\n"<< "<body>\n"<< "<h1>Request count</h1>\n"<< "<p>There have been "<< my_program_state::request_count()<< " requests so far.</p>\n"<< "</body>\n"<< "</html>\n";}else if (m_request.target() == "/time"){m_response.set(http::field::content_type, "text/html");beast::ostream(m_response.body())<< "<html>\n"<< "<head><title>Current time</title></head>\n"<< "<body>\n"<< "<h1>Current time</h1>\n"<< "<p>The current time is "<< my_program_state::now()<< " seconds since the epoch.</p>\n"<< "</body>\n"<< "</html>\n";}else{m_response.result(http::status::not_found);m_response.set(http::field::content_type, "text/plain");beast::ostream(m_response.body()) << "File not found\r\n";}
}
这个方法根据请求的目标生成不同的响应内容。
write_response
方法
void write_response()
{auto self = shared_from_this();m_response.content_length(m_response.body().size());http::async_write(m_socket, m_response, self {self->m_socket.shutdown(tcp::socket::shutdown_send, ec);self->m_deadline.cancel();});
}
这个方法异步写入HTTP响应,并在完成后关闭套接字。
create_post_response
方法
void create_post_response()
{if (m_request.target() == "/email"){auto& body = this->m_request.body();auto body_str = beast::buffers_to_string(body.data());std::cout << "receive body is " << body_str << std::endl;this->m_response.set(http::field::content_type, "text/json");Json::Value root;Json::Reader reader;Json::Value src_root;bool parse_success = reader.parse(body_str, src_root);if (!parse_success){std::cout << "Failed to parse Json data" << std::endl;root["error"] = 1001;std::string jsonstr = root.toStyledString();beast::ostream(m_response.body()) << jsonstr;return;}auto email = src_root["email"].asString();std::cout << "email is" << email << std::endl;root["error"] = 0;root["email"] = src_root["email"];root["msg"] = "receive email post success";std::string jsonstr = root.toStyledString();beast::ostream(m_response.body()) << jsonstr;}else{m_response.result(http::status::not_found);m_response.set(http::field::content_type, "text/plain");beast::ostream(m_response.body()) << "File not found\r\n";}
}
这个方法处理POST请求,解析JSON数据并生成响应。
方法概述
create_post_response
方法根据请求的目标路径处理POST请求。如果目标路径是 /email
,则解析请求体中的JSON数据,并生成相应的响应。如果目标路径不是 /email
,则返回404 Not Found响应。
代码解析
检查请求目标
if (m_request.target() == "/email")
这行代码检查HTTP请求的目标路径是否为 /email
。
获取请求体
auto& body = this->m_request.body();
auto body_str = beast::buffers_to_string(body.data());
std::cout << "receive body is " << body_str << std::endl;
auto& body = this->m_request.body();
获取请求体的引用。auto body_str = beast::buffers_to_string(body.data());
将请求体转换为字符串。std::cout << "receive body is " << body_str << std::endl;
打印接收到的请求体内容。
设置响应头
this->m_response.set(http::field::content_type, "text/json");
设置响应的内容类型为 text/json
。
解析JSON数据
Json::Value root;
Json::Reader reader;
Json::Value src_root;
bool parse_success = reader.parse(body_str, src_root);
if (!parse_success)
{std::cout << "Failed to parse Json data" << std::endl;root["error"] = 1001;std::string jsonstr = root.toStyledString();beast::ostream(m_response.body()) << jsonstr;return;
}
Json::Value root;
和Json::Value src_root;
定义两个JSON对象。Json::Reader reader;
定义一个JSON解析器。bool parse_success = reader.parse(body_str, src_root);
解析请求体中的JSON数据,并将结果存储在src_root
中。- 如果解析失败,设置错误信息并返回响应。
处理解析后的数据
auto email = src_root["email"].asString();
std::cout << "email is" << email << std::endl;
root["error"] = 0;
root["email"] = src_root["email"];
root["msg"] = "receive email post success";
std::string jsonstr = root.toStyledString();
beast::ostream(m_response.body()) << jsonstr;
auto email = src_root["email"].asString();
获取解析后的JSON数据中的email
字段。- 打印接收到的
email
。 - 设置响应的JSON数据,包括
error
、email
和msg
字段。 - 将响应的JSON数据转换为字符串,并写入响应体。
处理未找到的目标路径
else
{m_response.result(http::status::not_found);m_response.set(http::field::content_type, "text/plain");beast::ostream(m_response.body()) << "File not found\r\n";
}
如果请求的目标路径不是 /email
,则返回404 Not Found响应,并设置响应体为 “File not found”。
总结
create_post_response
方法处理HTTP POST请求,解析请求体中的JSON数据,并生成相应的响应。如果请求的目标路径不是 /email
,则返回404 Not Found响应。
http_server` 函数
void http_server(tcp::acceptor& acceptor, tcp::socket& socket)
{acceptor.async_accept(socket, & {if (!ec){std::make_shared<http_connection>(std::move(socket))->start();}http_server(acceptor, socket);});
}
这个函数接受新的连接并创建 http_connection
对象来处理每个连接。
main
函数
int main()
{try{auto const address = net::ip::make_address("127.0.0.1");unsigned short port = static_cast<unsigned short>(8080);net::io_context ioc{1};tcp::acceptor acceptor{ ioc,{address,port} };tcp::socket socket{ ioc };http_server(acceptor, socket);ioc.run();}catch (const std::exception& e){std::cerr << "Error: " << e.what() << std::endl;return EXIT_FAILURE;}
}
这个函数设置服务器的IP地址和端口,创建 io_context
、acceptor
和 socket
,并启动服务器。