forked from boostorg/beast
		
	fix #575, fix #604, fix #608, fix #634, fix #712 All examples are rewritten: * Using Best Practices * Mostly self-contained * New examples to complete the feature matrix * The server-framework example is removed
		
			
				
	
	
		
			244 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
// Copyright (c) 2016-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)
 | 
						|
//
 | 
						|
// Official repository: https://github.com/boostorg/beast
 | 
						|
//
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
//
 | 
						|
// Example: WebSocket server, asynchronous
 | 
						|
//
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
#include <boost/beast/core.hpp>
 | 
						|
#include <boost/beast/websocket.hpp>
 | 
						|
#include <boost/asio/strand.hpp>
 | 
						|
#include <boost/asio/ip/tcp.hpp>
 | 
						|
#include <algorithm>
 | 
						|
#include <cstdlib>
 | 
						|
#include <functional>
 | 
						|
#include <iostream>
 | 
						|
#include <memory>
 | 
						|
#include <string>
 | 
						|
#include <thread>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
 | 
						|
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
// Report a failure
 | 
						|
void
 | 
						|
fail(boost::system::error_code ec, char const* what)
 | 
						|
{
 | 
						|
    std::cerr << what << ": " << ec.message() << "\n";
 | 
						|
}
 | 
						|
 | 
						|
// Echoes back all received WebSocket messages
 | 
						|
class session : public std::enable_shared_from_this<session>
 | 
						|
{
 | 
						|
    websocket::stream<tcp::socket> ws_;
 | 
						|
    boost::asio::io_service::strand strand_;
 | 
						|
    boost::beast::multi_buffer buffer_;
 | 
						|
 | 
						|
public:
 | 
						|
    // Take ownership of the socket
 | 
						|
    explicit
 | 
						|
    session(tcp::socket socket)
 | 
						|
        : ws_(std::move(socket))
 | 
						|
        , strand_(ws_.get_io_service())
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    // Start the asynchronous operation
 | 
						|
    void
 | 
						|
    run()
 | 
						|
    {
 | 
						|
        // Accept the websocket handshake
 | 
						|
        ws_.async_accept(
 | 
						|
            strand_.wrap(std::bind(
 | 
						|
                &session::on_accept,
 | 
						|
                shared_from_this(),
 | 
						|
                std::placeholders::_1)));
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    on_accept(boost::system::error_code ec)
 | 
						|
    {
 | 
						|
        if(ec)
 | 
						|
            return fail(ec, "accept");
 | 
						|
 | 
						|
        // Read a message
 | 
						|
        do_read();
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    do_read()
 | 
						|
    {
 | 
						|
        // Read a message into our buffer
 | 
						|
        ws_.async_read(
 | 
						|
            buffer_,
 | 
						|
            strand_.wrap(std::bind(
 | 
						|
                &session::on_read,
 | 
						|
                shared_from_this(),
 | 
						|
                std::placeholders::_1)));
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    on_read(boost::system::error_code ec)
 | 
						|
    {
 | 
						|
        // This indicates that the session was closed
 | 
						|
        if(ec == websocket::error::closed)
 | 
						|
            return;
 | 
						|
 | 
						|
        if(ec)
 | 
						|
            fail(ec, "read");
 | 
						|
 | 
						|
        // Echo the message
 | 
						|
        ws_.text(ws_.got_text());
 | 
						|
        ws_.async_write(
 | 
						|
            buffer_.data(),
 | 
						|
            strand_.wrap(std::bind(
 | 
						|
                &session::on_write,
 | 
						|
                shared_from_this(),
 | 
						|
                std::placeholders::_1)));
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    on_write(boost::system::error_code ec)
 | 
						|
    {
 | 
						|
        if(ec)
 | 
						|
            return fail(ec, "write");
 | 
						|
 | 
						|
        // Clear the buffer
 | 
						|
        buffer_.consume(buffer_.size());
 | 
						|
 | 
						|
        // Do another read
 | 
						|
        do_read();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
// Accepts incoming connections and launches the sessions
 | 
						|
class listener : public std::enable_shared_from_this<listener>
 | 
						|
{
 | 
						|
    boost::asio::io_service::strand strand_;
 | 
						|
    tcp::acceptor acceptor_;
 | 
						|
    tcp::socket socket_;
 | 
						|
 | 
						|
public:
 | 
						|
    listener(
 | 
						|
        boost::asio::io_service& ios,
 | 
						|
        tcp::endpoint endpoint)
 | 
						|
        : strand_(ios)
 | 
						|
        , acceptor_(ios)
 | 
						|
        , socket_(ios)
 | 
						|
    {
 | 
						|
        boost::system::error_code ec;
 | 
						|
 | 
						|
        // Open the acceptor
 | 
						|
        acceptor_.open(endpoint.protocol(), ec);
 | 
						|
        if(ec)
 | 
						|
        {
 | 
						|
            fail(ec, "open");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        // Bind to the server address
 | 
						|
        acceptor_.bind(endpoint, ec);
 | 
						|
        if(ec)
 | 
						|
        {
 | 
						|
            fail(ec, "bind");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        // Start listening for connections
 | 
						|
        acceptor_.listen(
 | 
						|
            boost::asio::socket_base::max_connections, ec);
 | 
						|
        if(ec)
 | 
						|
        {
 | 
						|
            fail(ec, "listen");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Start accepting incoming connections
 | 
						|
    void
 | 
						|
    run()
 | 
						|
    {
 | 
						|
        if(! acceptor_.is_open())
 | 
						|
            return;
 | 
						|
        do_accept();
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    do_accept()
 | 
						|
    {
 | 
						|
        acceptor_.async_accept(
 | 
						|
            socket_,
 | 
						|
            strand_.wrap(std::bind(
 | 
						|
                &listener::on_accept,
 | 
						|
                shared_from_this(),
 | 
						|
                std::placeholders::_1)));
 | 
						|
    }
 | 
						|
 | 
						|
    void
 | 
						|
    on_accept(boost::system::error_code ec)
 | 
						|
    {
 | 
						|
        if(ec)
 | 
						|
        {
 | 
						|
            fail(ec, "accept");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Create the session and run it
 | 
						|
            std::make_shared<session>(std::move(socket_))->run();
 | 
						|
        }
 | 
						|
 | 
						|
        // Accept another connection
 | 
						|
        do_accept();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
int main(int argc, char* argv[])
 | 
						|
{
 | 
						|
    // Check command line arguments.
 | 
						|
    if (argc != 4)
 | 
						|
    {
 | 
						|
        std::cerr <<
 | 
						|
            "Usage: websocket-server-async <address> <port> <threads>\n" <<
 | 
						|
            "Example:\n" <<
 | 
						|
            "    websocket-server-async 0.0.0.0 8080 1\n";
 | 
						|
        return EXIT_FAILURE;
 | 
						|
    }
 | 
						|
    auto const address = boost::asio::ip::address::from_string(argv[1]);
 | 
						|
    auto const port = static_cast<unsigned short>(std::atoi(argv[2]));
 | 
						|
    auto const threads = std::max<std::size_t>(1, std::atoi(argv[3]));
 | 
						|
 | 
						|
    // The io_service is required for all I/O
 | 
						|
    boost::asio::io_service ios{threads};
 | 
						|
 | 
						|
    // Create and launch a listening port
 | 
						|
    std::make_shared<listener>(ios, tcp::endpoint{address, port})->run();
 | 
						|
 | 
						|
    // Run the I/O service on the requested number of threads
 | 
						|
    std::vector<std::thread> v;
 | 
						|
    v.reserve(threads - 1);
 | 
						|
    for(auto i = threads - 1; i > 0; --i)
 | 
						|
        v.emplace_back(
 | 
						|
        [&ios]
 | 
						|
        {
 | 
						|
            ios.run();
 | 
						|
        });
 | 
						|
    ios.run();
 | 
						|
 | 
						|
    return EXIT_SUCCESS;
 | 
						|
}
 |