基于beast库搭建http服务器,并实现get和post请求交互(附源代码)

1 Beast简介

Beast是Boost中关于http(s)/websocket(s)的库,首发于boost 1.66(2016年),是比较新的库,它主要包含了http、websocket协议的解析(反序列化)和封装(序列化)以及关于网络的操作,它以asio为基础,但似乎又想隔离Asio。本文从0到1详细描述其搭建步骤以及思路。

2 搭建流程

2.1 boost和jsoncpp下载和安装

提前下载好这两个,下载和准备工作,参下面这两篇文章,
Windows下Boost库的安装与使用
C++上Json的使用说明(从下载到生成库再到使用)

2.2 项目创建和配置

1、打开visual studio,创建一个控制台应用,空项目也可以,项目名称和位置随意。
2、配置项目,右键项目,打开属性,点击左侧的VC++目录,选择包含目录选项,如下编辑配置,
在这里插入图片描述
在这里插入图片描述
3、同上,再次编辑配置库目录,见下图
在这里插入图片描述
4、配置jsoncpp库文件,按照如下顺序进行配置,第四步替换为自己的jsoncpp路径即可。
在这里插入图片描述
在这里插入图片描述
至此,所有配置完成。

2.3 代码

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio.hpp>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <memory>
#include <string>
#include<json/json.h>
#include<json/value.h>
#include<json/reader.h>
namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>

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);
    }
}

class http_connection : public std::enable_shared_from_this<http_connection>
{
    
    
public:
    http_connection(tcp::socket socket)
        : socket_(std::move(socket))
    {
    
    
    }

    // Initiate the asynchronous operations associated with the connection.
    void
        start()
    {
    
    
        read_request();
        check_deadline();
    }

private:
    // The socket for the currently connected client.
    tcp::socket socket_;

    // The buffer for performing reads.
    beast::flat_buffer buffer_{
    
    8192};

    // The request message.
    http::request<http::dynamic_body> request_;

    // The response message.
    http::response<http::dynamic_body> response_;

    // The timer for putting a deadline on connection processing.
    net::steady_timer deadline_{
    
    
        socket_.get_executor(), std::chrono::seconds(60)};

    // Asynchronously receive a complete request message.
    void
        read_request()
    {
    
    
        auto self = shared_from_this();

        http::async_read(
            socket_,
            buffer_,
            request_,
            [self](beast::error_code ec,
                std::size_t bytes_transferred)
            {
    
    
                boost::ignore_unused(bytes_transferred);
                if (!ec)
                    self->process_request();
            });
    }

    // Determine what needs to be done with the request message.
    void
        process_request()
    {
    
    
        response_.version(request_.version());
        response_.keep_alive(false);

        switch (request_.method())
        {
    
    
        case http::verb::get:
            response_.result(http::status::ok);
            response_.set(http::field::server, "Beast");
            create_response();
            break;
        case http::verb::post:
            response_.result(http::status::ok);
            response_.set(http::field::server, "Beast");
            create_post_response();
            break;
        default:
            // We return responses indicating an error if
            // we do not recognize the request method.
            response_.result(http::status::bad_request);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body())
                << "Invalid request-method '"
                << std::string(request_.method_string())
                << "'";
            break;
        }

        write_response();
    }

    // Construct a response message based on the program state.
    void
        create_response()
    {
    
    
        if (request_.target() == "/count")
        {
    
    
            response_.set(http::field::content_type, "text/html");
            beast::ostream(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 (request_.target() == "/time")
        {
    
    
            response_.set(http::field::content_type, "text/html");
            beast::ostream(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
        {
    
    
            response_.result(http::status::not_found);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body()) << "File not found\r\n";
        }
    }

    // Asynchronously transmit the response message.
    void
        write_response()
    {
    
    
        auto self = shared_from_this();

        response_.content_length(response_.body().size());

        http::async_write(
            socket_,
            response_,
            [self](beast::error_code ec, std::size_t)
            {
    
    
                self->socket_.shutdown(tcp::socket::shutdown_send, ec);
                self->deadline_.cancel();
            });
    }

    // Check whether we have spent enough time on this connection.
    void
        check_deadline()
    {
    
    
        auto self = shared_from_this();

        deadline_.async_wait(
            [self](beast::error_code ec)
            {
    
    
                if (!ec)
                {
    
    
                    // Close socket to cancel any outstanding operation.
                    self->socket_.close(ec);
                }
            });
    }
    void create_post_response() {
    
    
        if (request_.target() == "/email")
        {
    
    
            auto& body = this->request_.body();
            auto body_str = boost::beast::buffers_to_string(body.data());
            std::cout << "receive body is " << body_str << std::endl;
            this->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(this->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"] = "recevie email post success";
            std::string jsonstr = root.toStyledString();
            beast::ostream(this->response_.body()) << jsonstr;
        }
        else
        {
    
    
            response_.result(http::status::not_found);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body()) << "File not found\r\n";
        }
    }
};

void
http_server(tcp::acceptor& acceptor, tcp::socket& socket)
{
    
    
    acceptor.async_accept(socket,
        [&](beast::error_code ec)
        {
    
    
            if (!ec)
                std::make_shared<http_connection>(std::move(socket))->start();
            http_server(acceptor, socket);
        });
}

int main(int argc, char* argv[])
{
    
    
    try
    {
    
    
        // Check command line arguments.
        /*if (argc != 3)
        {
            std::cerr << "Usage: " << argv[0] << " <address> <port>\n";
            std::cerr << "  For IPv4, try:\n";
            std::cerr << "    receiver 0.0.0.0 80\n";
            std::cerr << "  For IPv6, try:\n";
            std::cerr << "    receiver 0::0 80\n";
            return EXIT_FAILURE;
        }*/

        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 (std::exception const& e)
    {
    
    
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}

2.4 测试

get请求,url为localhost:8080/time
get请求
post请求,url为localhost:8080/email
post请求

3 源代码

点这里获取源代码

猜你喜欢

转载自blog.csdn.net/qq_52431815/article/details/131832690
今日推荐