forked from boostorg/beast
		
	
		
			
	
	
		
			259 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			259 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Distributed under the Boost Software License, Version 1.0. (See accompanying
							 | 
						||
| 
								 | 
							
								// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef BEAST_EXAMPLE_FRAMEWORK_SERVER_HPP
							 | 
						||
| 
								 | 
							
								#define BEAST_EXAMPLE_FRAMEWORK_SERVER_HPP
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "framework.hpp"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <boost/optional.hpp>
							 | 
						||
| 
								 | 
							
								#include <memory>
							 | 
						||
| 
								 | 
							
								#include <string>
							 | 
						||
| 
								 | 
							
								#include <stdexcept>
							 | 
						||
| 
								 | 
							
								#include <thread>
							 | 
						||
| 
								 | 
							
								#include <type_traits>
							 | 
						||
| 
								 | 
							
								#include <utility>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace framework {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/** A server instance that accepts TCP/IP connections.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This is a general purpose TCP/IP server which contains
							 | 
						||
| 
								 | 
							
								    zero or more user defined "ports". Each port represents
							 | 
						||
| 
								 | 
							
								    a listening socket whose behavior is defined by an
							 | 
						||
| 
								 | 
							
								    instance of the @b PortHandler concept.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    To use the server, construct the class and then add the
							 | 
						||
| 
								 | 
							
								    ports that you want using @ref make_port.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @par Example
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @code
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Create a server with 4 threads 
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    framework::server si(4);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Create a port that echoes everything back.
							 | 
						||
| 
								 | 
							
								    // Bind all available interfaces on port 1000.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    framework::error_code ec;
							 | 
						||
| 
								 | 
							
								    si.make_port<echo_port>(
							 | 
						||
| 
								 | 
							
								        ec,
							 | 
						||
| 
								 | 
							
								        server::endpoint_type{
							 | 
						||
| 
								 | 
							
								            server::address_type::from_string("0.0.0.0"), 1000}
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Close all connections, shut down the server
							 | 
						||
| 
								 | 
							
								    si.stop();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @endcode
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								class server
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    io_service_type ios_;
							 | 
						||
| 
								 | 
							
								    std::vector<std::thread> tv_;
							 | 
						||
| 
								 | 
							
								    boost::optional<boost::asio::io_service::work> work_;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
								    server(server const&) = delete;
							 | 
						||
| 
								 | 
							
								    server& operator=(server const&) = delete;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /** Constructor
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @param n The number of threads to run on the `io_service`,
							 | 
						||
| 
								 | 
							
								        which must be greater than zero.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    explicit
							 | 
						||
| 
								 | 
							
								    server(std::size_t n = 1)
							 | 
						||
| 
								 | 
							
								        : work_(ios_)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if(n < 1)
							 | 
						||
| 
								 | 
							
								            throw std::invalid_argument{"threads < 1"};
							 | 
						||
| 
								 | 
							
								        tv_.reserve(n);
							 | 
						||
| 
								 | 
							
								        while(n--)
							 | 
						||
| 
								 | 
							
								            tv_.emplace_back(
							 | 
						||
| 
								 | 
							
								                [&]
							 | 
						||
| 
								 | 
							
								                {
							 | 
						||
| 
								 | 
							
								                    ios_.run();
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /** Destructor
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Upon destruction, the `io_service` will be stopped
							 | 
						||
| 
								 | 
							
								        and all pending completion handlers destroyed.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    ~server()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        work_ = boost::none;
							 | 
						||
| 
								 | 
							
								        ios_.stop();
							 | 
						||
| 
								 | 
							
								        for(auto& t : tv_)
							 | 
						||
| 
								 | 
							
								            t.join();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /// Return the `io_service` associated with the server
							 | 
						||
| 
								 | 
							
								    boost::asio::io_service&
							 | 
						||
| 
								 | 
							
								    get_io_service()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return ios_;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /** Return a new, small integer unique id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        These ids are used to uniquely identify connections
							 | 
						||
| 
								 | 
							
								        in log output.
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    std::size_t
							 | 
						||
| 
								 | 
							
								    next_id()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        static std::atomic<std::size_t> id_{0};
							 | 
						||
| 
								 | 
							
								        return ++id_;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /** Create a listening port.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @param ec Set to the error, if any occurred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @param ep The address and port to bind to.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @param args Optional arguments, forwarded to the
							 | 
						||
| 
								 | 
							
								        port handler's constructor.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @tparam PortHandler The port handler to use for handling
							 | 
						||
| 
								 | 
							
								        incoming connections on this port. This handler must meet
							 | 
						||
| 
								 | 
							
								        the requirements of @b PortHandler. A model of PortHandler
							 | 
						||
| 
								 | 
							
								        is as follows:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @code
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        struct PortHandler
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            void
							 | 
						||
| 
								 | 
							
								            on_accept(
							 | 
						||
| 
								 | 
							
								                endpoint_type ep,       // address of the remote endpoint
							 | 
						||
| 
								 | 
							
								                socket_type&& sock,     // the connected socket
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @endcode
							 | 
						||
| 
								 | 
							
								    */
							 | 
						||
| 
								 | 
							
								    template<class PortHandler, class... Args>
							 | 
						||
| 
								 | 
							
								    std::shared_ptr<PortHandler>
							 | 
						||
| 
								 | 
							
								    make_port(
							 | 
						||
| 
								 | 
							
								        error_code& ec,
							 | 
						||
| 
								 | 
							
								        endpoint_type const& ep,
							 | 
						||
| 
								 | 
							
								        Args&&... args);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								template<class PortHandler>
							 | 
						||
| 
								 | 
							
								class port
							 | 
						||
| 
								 | 
							
								    : public std::enable_shared_from_this<
							 | 
						||
| 
								 | 
							
								        port<PortHandler>>
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    server& instance_;
							 | 
						||
| 
								 | 
							
								    PortHandler handler_;
							 | 
						||
| 
								 | 
							
								    endpoint_type ep_;
							 | 
						||
| 
								 | 
							
								    strand_type strand_;
							 | 
						||
| 
								 | 
							
								    acceptor_type acceptor_;
							 | 
						||
| 
								 | 
							
								    socket_type sock_;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
								    // Constructor
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // args are forwarded to the PortHandler
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    template<class... Args>
							 | 
						||
| 
								 | 
							
								    explicit
							 | 
						||
| 
								 | 
							
								    port(server& instance, Args&&... args)
							 | 
						||
| 
								 | 
							
								        : instance_(instance)
							 | 
						||
| 
								 | 
							
								        , handler_(std::forward<Args>(args)...)
							 | 
						||
| 
								 | 
							
								        , strand_(instance.get_io_service())
							 | 
						||
| 
								 | 
							
								        , acceptor_(instance.get_io_service())
							 | 
						||
| 
								 | 
							
								        , sock_(instance.get_io_service())
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Return the PortHandler wrapped in a shared_ptr
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    std::shared_ptr<PortHandler>
							 | 
						||
| 
								 | 
							
								    handler()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // This uses a feature of std::shared_ptr invented by
							 | 
						||
| 
								 | 
							
								        // Peter Dimov where the managed object piggy backs off
							 | 
						||
| 
								 | 
							
								        // the reference count of another object containing it.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        return std::shared_ptr<PortHandler>(
							 | 
						||
| 
								 | 
							
								            this->shared_from_this(), &handler_);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Open the listening socket
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    void
							 | 
						||
| 
								 | 
							
								    open(endpoint_type const& ep, error_code& ec)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        acceptor_.open(ep.protocol(), ec);
							 | 
						||
| 
								 | 
							
								        if(ec)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        acceptor_.set_option(
							 | 
						||
| 
								 | 
							
								            boost::asio::socket_base::reuse_address{true});
							 | 
						||
| 
								 | 
							
								        acceptor_.bind(ep, ec);
							 | 
						||
| 
								 | 
							
								        if(ec)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        acceptor_.listen(
							 | 
						||
| 
								 | 
							
								            boost::asio::socket_base::max_connections, ec);
							 | 
						||
| 
								 | 
							
								        if(ec)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        acceptor_.async_accept(sock_, ep_,
							 | 
						||
| 
								 | 
							
								            std::bind(&port::on_accept, this->shared_from_this(),
							 | 
						||
| 
								 | 
							
								                std::placeholders::_1));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								private:
							 | 
						||
| 
								 | 
							
								    // Called when an incoming connection is accepted
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    void
							 | 
						||
| 
								 | 
							
								    on_accept(error_code ec)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if(! acceptor_.is_open())
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        if(ec == boost::asio::error::operation_aborted)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        if(! ec)
							 | 
						||
| 
								 | 
							
								            handler_.on_accept(std::move(sock_), ep_);
							 | 
						||
| 
								 | 
							
								        acceptor_.async_accept(sock_, ep_,
							 | 
						||
| 
								 | 
							
								            std::bind(&port::on_accept, this->shared_from_this(),
							 | 
						||
| 
								 | 
							
								                std::placeholders::_1));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								template<class PortHandler, class... Args>
							 | 
						||
| 
								 | 
							
								std::shared_ptr<PortHandler>
							 | 
						||
| 
								 | 
							
								server::
							 | 
						||
| 
								 | 
							
								make_port(
							 | 
						||
| 
								 | 
							
								    error_code& ec,
							 | 
						||
| 
								 | 
							
								    endpoint_type const& ep,
							 | 
						||
| 
								 | 
							
								    Args&&... args)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    auto sp = std::make_shared<port<PortHandler>>(
							 | 
						||
| 
								 | 
							
								        *this, std::forward<Args>(args)...);
							 | 
						||
| 
								 | 
							
								    sp->open(ep, ec);
							 | 
						||
| 
								 | 
							
								    if(ec)
							 | 
						||
| 
								 | 
							
								        return nullptr;
							 | 
						||
| 
								 | 
							
								    return sp->handler();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								} // framework
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif
							 |