Coroutine And Client

Coroutine and client

Here is a coroutine version of the client code using Boost.Coroutine. I use asymmetric coroutine:
http://www.boost.org/libs/coroutine/doc/html/coroutine/coroutine/asymmetric.html

#define _WEBSOCKETPP_CPP11_STL_
 
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/client.hpp>
 
#include <boost/coroutine/all.hpp>
 
int main() {
    using client = websocketpp::client<websocketpp::config::asio>;
 
    client::connection_ptr con;
 
    // Execute the first command.
    boost::coroutines::asymmetric_coroutine<std::string const&>::pull_type source(
        [&con](boost::coroutines::asymmetric_coroutine<std::string const&>::push_type& sink){
            // Start point
            std::string const url = "ws://localhost:12345/";
 
            client c;
 
            // disable log output
            c.clear_access_channels(websocketpp::log::alevel::all);
 
            boost::asio::io_service ios;
            c.init_asio(&ios);
 
            c.set_open_handler(
                [&con, &sink](websocketpp::connection_hdl) {
                    std::cout << "connected" << std::endl;
                    sink(""); // Jump to #1
                    // #2
                });
            c.set_message_handler(
                [&con, &sink](websocketpp::connection_hdl, client::message_ptr const& msg) {
                    std::cout << msg->get_payload() << " received" << std::endl;
                    sink(msg->get_payload()); // Jump to #3, #5
                    // #4, #6
                });
            c.set_close_handler(
                [&sink](websocketpp::connection_hdl) {
                    std::cout << "closed" << std::endl;
                    sink(""); // Jump to #7
                });
 
            websocketpp::lib::error_code ec;
            con = c.get_connection(url, ec);
            std::cout << ec.message() << std::endl;
 
            c.connect(con);
 
            ios.run();
        });
 
    // Application logics are here
    // #1 execute second command.
    con->send(std::string("hello"), websocketpp::frame::opcode::text);
    source(); // Jump to #2
 
    // #3 print received message, and send "stop listening".
    std::cout << "[outside]" << source.get() << " received" << std::endl;
    con->send(std::string("stop listening"), websocketpp::frame::opcode::text);
    source(); // Jump to #4
 
    // #5 print received message, and close connection.
    std::cout << "[outside]" << source.get() << " received" << std::endl;
    con->close(websocketpp::close::status::normal, "");
    source(); // Jump to #6
 
    // #7
}

Compile

g++ -std=c++1y simple_client.cpp -o simple_client -lboost_system -pthread -lboost_coroutine

How coroutine works

The program start from main(), then execute the argument lambda function of the source(). The lambda function start from "Start point", then call ios.run(). When echo_server accepted the connection request, open_handler is called from ios.run(). In the open_handler, sink("") is called, then the context jump to "#1". "#1" is the start point of the application logic. When source() is called, the context jump to just after the previous sink() called point, #2. Similarlly, context switches again and again between sink() and source().

We can pass the information from sink to source. The type of the information is defined as follows:

    boost::coroutines::asymmetric_coroutine<std::string const&>::pull_type source(
        [&con](boost::coroutines::asymmetric_coroutine<std::string const&>::push_type& sink){

std::string const& is the type of the information. The caller of sink() can pass the information as an argument. The receiver can get the information by calling source.get().

You can see the information propergation in the following output. The message start with "[outsite]" prefix is that:

Client output:

Success
connected
hello received
[outside]hello received
stop listening received
[outside]stop listening received
closed

Echo server output:

hello
stop listening
[2015-02-22 20:27:07] [info] asio async_shutdown error: system:9 (Bad file descriptor)
[2015-02-22 20:27:07] [info] handle_accept error: Operation canceled
[2015-02-22 20:27:07] [info] Stopping acceptance of new connections because the underlying transport is no longer listening.