Beast websocket client async

From kangssu
Jump to: navigation, search

beast library

Beast is one of the c++ network library types. beast library belongs to boost libraries(Everyone knows boost).

Beast is a C++ header-only library which provides low-level HTTP[s], Websocket, and networking protocols. Also it supports asynchronous model of boost::asio.

I will deal with asynchoronous model because power of boost::asio comes from asynchronous model.

Boost 1.66.0 includes beast library.

You can get boost libraries at boost official site. here

beast websocket async example

#pragma once
#include <boost/beast/websocket.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>
static void fail2(boost::system::error_code ec, char const* what) {
    std::cerr << what << ": " << ec.message() << "\n";
}

// Sends a WebSocket message and prints the response
class SignalSession : public std::enable_shared_from_this<SignalSession>
{
public:
    tcp::resolver resolver_;
    websocket::stream<tcp::socket> ws_;
    boost::beast::multi_buffer buffer_;
    std::string host_;
    std::string text_;

public:
    // Resolver and socket require an io_service
    explicit
    SignalSession(boost::asio::io_service& ioc) : resolver_(ioc) , ws_(ioc) {
    }

    // Start the asynchronous operation
    void connectSignalServer( char const* host, char const* port, char const* text) {
        // Save these for later
        host_ = host;
        text_ = text;

        // Look up the domain name
        resolver_.async_resolve({host, port},
                                std::bind(
                                    &SignalSession::on_resolve,
                                    shared_from_this(),
                                    std::placeholders::_1,
                                    std::placeholders::_2));
        //resolver_.async_resolve(
            //{host, port},
            //boost::bind(&SignalSession::on_resolve, shared_from_this(), 
                        //boost::asio::placeholders::error, 
                        //boost::asio::placeholders::results));

            //boost::bind(
                //&SignalSession::on_resolve,
                //shared_from_this(),
                //std::placeholders::_1,
                //std::placeholders::_2));
    }

    void on_resolve( boost::system::error_code ec, tcp::resolver::iterator result) {
        if(ec) {
            return fail2(ec, "resolve");
        }

        // Make the connection on the IP address we get from a lookup
        boost::asio::async_connect(
            ws_.next_layer(),
            result,
            std::bind(
                &SignalSession::on_connect,
                shared_from_this(),
                std::placeholders::_1));
    } 

    void on_connect(boost::system::error_code ec) {
        if(ec) {
            return fail2(ec, "connect");
        }

        auto self1 = shared_from_this();
        auto self2 = shared_from_this();
        ws_.async_handshake_ex(host_, "/", 

                        [self1](websocket::request_type& req) {
                            req.insert("auth", "someAuth");
                        },
                        [self2](boost::system::error_code ec) {
                            if(ec) {
                                return fail(ec, "handshake");
                            }
                        });
    } 

    void on_handshake(boost::system::error_code ec) {
        if(ec)
        {
            return fail2(ec, "handshake");
        }

        ws_.async_read(
            buffer_,
            std::bind(
                &SignalSession::on_read,
                shared_from_this(),
                std::placeholders::_1,
                std::placeholders::_2));
        // Send the message
        //
        ws_.async_write(
            boost::asio::buffer(text_),
            std::bind(
                &SignalSession::on_write,
                shared_from_this(),
                std::placeholders::_1,
                std::placeholders::_2));
    }
    void on_write( boost::system::error_code ec, std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if(ec) {
            return fail2(ec, "write");
        }
        
        // Read a message into our buffer
    }

    void on_read( boost::system::error_code ec, std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if(ec) {
            return fail2(ec, "read");
        }

        std::cout << "received: " << boost::beast::buffers(buffer_.data()) << std::endl << "---------------\n";
        buffer_.consume(buffer_.size());
        static int i = 0;
        std::string sendingText = text_ + std::to_string(i++);
        ws_.async_write(
            boost::asio::buffer(sendingText),
            boost::bind(
                &SignalSession::on_write,
                shared_from_this(),
                boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred));

        ws_.async_read(
            buffer_,
            boost::bind(
                &SignalSession::on_read,
                shared_from_this(),
                boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred));
        // Close the WebSocket connection
    }

    void on_close(boost::system::error_code ec) {
        if(ec) {
            return fail2(ec, "close");
        }

        // If we get here then the connection is closed gracefully

        // The buffers() function helps print a ConstBufferSequence
        std::cout << boost::beast::buffers(buffer_.data()) << std::endl;
    }
};

using lambda instead of boost::bind

auto self = shared_from_this();
ws_.async_write(
    boost::asio::buffer(sendingText),
    [self](boost::system::error_code ec,
std::size_t bytes_transferred) {
	std::cout << "send data size: " << bytes_transferred << std::endl; 
    });

Note that auto self = shared_from_this();

You have to shared_from_this() to avoid io_service's terminating.