Capnproto - Make client request in server callback - c++

I need to do some client-requests inside a server-callback and are not sure where to store the created capnp::EzRpcClient and CompareNetwork::Client comparer objects. That's because the clients go out of scope (I think - I just get a SEGFAULT, but that seems to be the reason). It's basically a master who forwards a loading request to it's slaves (slaves can register via a REG-request and their address is stored).
So - where/how should I store the client objects? Is there any "best-practice" with this? I think temporarily storing them in some class member variable is a little dirty, isn't it?
GroupMaster2.cpp:
kj::Promise<void> GroupMaster2::CompareMasterImpl::load(LoadContext context) {
auto loadData = context.getParams().getIds();
slaveListLock.lock();
auto promList = kj::Vector<kj::Promise<std::pair<std::string, CompareNetwork::Status>>>();
for(auto& slave : slaveList) {
try {
capnp::EzRpcClient client(slave.second->address);
CompareNetwork::Client comparer = client.getMain<CompareNetwork>();
auto request = comparer.loadRequest();
std::string addrCopy(slave.first);
request.setIds(loadData);
auto loadPromise = request.send();
promList.add(loadPromise.then([addrCopy](auto response) {
return std::make_pair(addrCopy, response.getStatus());
},
[&, addrCopy](kj::Exception && exception) {
slaveListLock.lock();
slaveList.erase(addrCopy);//something failed, remove this slave!
slaveListLock.unlock();
std::cout << "ErrLoad0: " << std::string(exception.getDescription()) << std::endl;
return std::make_pair(addrCopy, CompareNetwork::Status::ERROR);
}));
}
catch(...) {
std::cout << "Error sending load to: " << slave.first << std::endl;
}
}
slaveListLock.unlock();
auto retProm = kj::joinPromises(promList.releaseAsArray()).then([&, KJ_CPCAP(context)](kj::Array<std::pair<std::string, CompareNetwork::Status>> res) mutable {
bool error = false;
for(auto& loadRes : res) {
switch(loadRes.second) {
case CompareNetwork::Status::OK: {
std::cout << "LOAD OK: " << loadRes.first << std::endl;
break;
}
case CompareNetwork::Status::ERROR: {
std::cout << "LOAD ERROR: " << loadRes.first << std::endl;
error = true;
break;
}
}
}
if(!error)
context.getResults().setStatus(CompareNetwork::Status::OK);
else
context.getResults().setStatus(CompareNetwork::Status::ERROR);
}, [](kj::Exception && exception) {
std::cout << __FILE__ << ":" << __LINE__ << std::string(exception.getDescription()) << std::endl;
}).eagerlyEvaluate([](kj::Exception && exception) {
std::cout << __FILE__ << ":" << __LINE__ << std::string(exception.getDescription()) << std::endl;
});
std::cout << "ReturnedLoad" << std::endl;
return retProm;
}
GroupNetworkData.capnp:
interface CompareNetwork {
compare #0 (jobs :JobPartList) -> (res :JobResList);
load #1 (ids :WIDList) -> (status :Status);
struct JobPartList {
jobParts #0 :List(JobPart);
struct JobPart {
jobs #0 :List(Job);
startID #1 :UInt32;
struct Job {
wid1 #0 :UInt32;
wid2 #1 :UInt32;
}
}
}
struct JobResList {
jobResults #0 :List(JobRes);
struct JobRes {
jobIndex #0 :UInt32;
result #1 :Float64;
}
}
struct WIDList {
ids #0 :List(WID);
struct WID {
id #0 :UInt32;
}
}
enum Status {
ok #0;
error #1;
}
}
interface CompareMaster extends(CompareNetwork) {
reg #0 (data :SlaveData) -> (status :CompareNetwork.Status);
struct SlaveData {
perfInd #0 :Float64;
maxMem #1 :UInt32;
address #2 :Text;
}
}
Thanks in advance!
dvs23

C++14 seems to have the answer:
How to capture a unique_ptr into a lambda expression?
Allocate both objects on heap with kj::heap and move the kj::Own to the callback lambda:
for(auto& slave : slaveList) {
try {
auto client = kj::heap<capnp::EzRpcClient>(slave.second->address);
auto comparer = kj::heap<CompareNetwork::Client>(client->getMain<CompareNetwork>());
auto request = comparer->loadRequest();
std::string addrCopy(slave.first);
request.setIds(loadData);
auto loadPromise = request.send();
promList.add(loadPromise.then([addrCopy, client = std::move(client), comparer = std::move(comparer)](auto response) {
return std::make_pair(addrCopy, response.getStatus());
},
[&, addrCopy](kj::Exception && exception) {
slaveListLock.lock();
slaveList.erase(addrCopy);//something failed, remove this slave!
slaveListLock.unlock();
std::cout << "ErrLoad0: " << std::string(exception.getDescription()) << std::endl;
return std::make_pair(addrCopy, CompareNetwork::Status::ERROR);
}));
}
catch(...) {
std::cout << "Error sending load to: " << slave.first << std::endl;
}
}
Better ideas are welcome, but at the moment it seems to work pretty well :)

Related

Poco Network TCP: how to check client disconnect not through an exception

First time dealing with Poco. So I have some questions. Great framework but it has no documentation at all. Official presentation is just a useless booklet.
Is accept_connections() ok?
Got an exception when peer disconnects, but want to be able to check some error or anything else to handle it properly.
And may be I should rewrite my for_range(peers) to something else? It eats CPU by 100%. Can't find anything on official site.
void accept_connections ()
{
while (is_acceptor_on)
{
if (socket)
{
if (socket->poll(Poco::Timespan(1000), Poco::Net::Socket::SELECT_READ))
{
auto it = peers.emplace(
peers.size()+1, // for id
std::make_shared<Poco::Net::StreamSocket>(
std::move( socket->acceptConnection() )
)
);
std::cout << "server peer connected #" << peers.size() << std::endl;
}
}
}
}
void work()
{
while (is_work_on)
{
for (auto& i : peers)
{
try
{
if (i.second->lastError() == 0)
{
if (i.second->poll(Poco::Timespan(100), Poco::Net::Socket::SELECT_READ))
{
if (i.second->available() > 0)
{
}
}
}
else if (i.second->lastError() == 107)
{
std::cout << i.second->lastErrorDesc() << " disconnected" << std::endl;
}
}
catch (const Poco::Exception& e)
{
std::cout << e.displayText() << std::endl;
}
}
}
}

Use `std::ofstream` with opentelemetry `OStreamSpanExporter`

I want to create an opentelemetry tracing configuration that writes the exporter logs to a file. For that, I used the OStreamSpanExporter class that takes a ref to a std::ostream object (by default, the ctor argument is std::cout). So here is what I did:
#include <fstream>
namespace trace_sdk = opentelemetry::sdk::trace;
std::ofstream file_handle(log_trace_output_file_.c_str());
auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(file_handle));
auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new trace_sdk::TracerProvider(std::move(processor)));
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
This compiles nicely. Before you ask, we checked that log_trace_output_file_.c_str() is not empty. However I encounter segmentation fault as soon as I start creating spans... Do you know what I might have been doing wrong here ? Thank you.
Ok, I realised that because of the std::move when declaring the processor, we were giving away the ownership thus we were trying to access a stream that was nullptr...
Here is what I ended up doing:
main.cpp
auto trace_provider = new TraceProvider(vm["trace-provider"].as<std::string>(), vm["trace-output-log-file"].as<std::string>());
trace_provider->InitTracer();
TraceProvider.hpp
class TraceProvider {
public:
TraceProvider(std::string exporter_backend_str, std::string log_trace_output_file = std::string());
~TraceProvider();
void InitTracer();
private:
std::string exporter_backend_str_;
std::string log_trace_output_file_;
std::shared_ptr<std::ofstream> log_trace_output_file_handle_ = nullptr;
void initSimpleTracer();
};
TraceProvider.cpp
TraceProvider::TraceProvider(std::string exporter_backend_str, std::string log_trace_output_file) {
exporter_backend_str_ = exporter_backend_str;
exporter_backend_ = string_to_trace_exporter(exporter_backend_str);
log_trace_output_file_ = log_trace_output_file;
if (exporter_backend_ == Exporter::SIMPLE) {
try {
if (log_trace_output_file_.compare("") != 0) {
log_trace_output_file_handle_ = std::make_shared<std::ofstream>(std::ofstream(log_trace_output_file.c_str()));
} else {
throw std::runtime_error("You chose the Simple trace exporter but you specified an empty log file.");
}
} catch(std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
}
}
}
TraceProvider::~TraceProvider() {
// If it exists, close the file stream and delete the ptr
if (log_trace_output_file_handle_ != nullptr) {
std::cout << "Closing tracing log file at: " << log_trace_output_file_ << std::endl;
log_trace_output_file_handle_.get()->close();
log_trace_output_file_handle_.reset();
log_trace_output_file_handle_ = nullptr;
}
}
void TraceProvider::InitTracer() {
switch (exporter_backend_) {
case Exporter::SIMPLE:
initSimpleTracer();
break;
case Exporter::JAEGER:
initJaegerTracer();
break;
default:
std::stringstream err_msg_stream;
err_msg_stream << "Invalid tracing backend: " << exporter_backend_str_
<< "\n";
throw po::validation_error(po::validation_error::invalid_option_value,
err_msg_stream.str());
}
}
void TraceProvider::initSimpleTracer() {
std::unique_ptr<trace_sdk::SpanExporter> exporter;
if (log_trace_output_file_.compare("") != 0)
exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(*log_trace_output_file_handle_.get()));
} else {
exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter);
}
auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new trace_sdk::TracerProvider(std::move(processor), resources));
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
// Set global propagator
context::propagation::GlobalTextMapPropagator::SetGlobalPropagator(
nostd::shared_ptr<context::propagation::TextMapPropagator>(
new opentelemetry::trace::propagation::HttpTraceContext()));
std::cout << "Simple (log stream) exporter successfully initialized!"
<< std::endl;
}

Making my function which calls async_read asynchronous Boost::asio

I am building an networking application, and being a newbie to Boost asio and networking as a whole had this doubt which might be trivial. I have this application which reads from a file and calls apis accordingly. I am reading json (example):
test.json
{
"commands":
[
{
"type":"login",
"Username": 0,
"Password": "kk"
}
]
}
My main program looks like this :
int main() {
ba::io_service ios;
tcp::socket s(ios);
s.connect({{},8080});
IO io;
io.start_read(s);
io.interact(s);
ios.run();
}
void start_read(tcp::socket& socket) {
char buffer_[MAX_LEN];
socket.async_receive(boost::asio::null_buffers(),
[&](const boost::system::error_code& ec, std::size_t bytes_read) {
(void)bytes_read;
if (likely(!ec)) {
boost::system::error_code errc;
int br = 0;
do {
br = socket.receive(boost::asio::buffer(buffer_, MAX_LEN), 0, errc);
if (unlikely(errc)) {
if (unlikely(errc != boost::asio::error::would_block)) {
if (errc != boost::asio::error::eof)
std::cerr << "asio async_receive: error " << errc.value() << " ("
<< errc.message() << ")" << std::endl;
interpret_read(socket,nullptr, -1);
//close(as);
return;
}
break; // EAGAIN
}
if (unlikely(br <= 0)) {
std::cerr << "asio async_receive: error, read " << br << " bytes" << std::endl;
interpret_read(socket,nullptr, br);
//close(as);
return;
}
interpret_read(socket,buffer_, br);
} while (br == (int)MAX_LEN);
} else {
if (socket.is_open())
std::cerr << "asio async_receive: error " << ec.value() << " (" << ec.message() << ")"
<< std::endl;
interpret_read(socket,nullptr, -1);
//close(as);
return;
}
start_read(socket);
});
}
void interpret_read(tcp::socket& s,const char* buf, int len) {
if(len<0)
{
std::cout<<"some error occured in reading"<<"\n";
}
const MessageHeaderOutComp *obj = reinterpret_cast<const MessageHeaderOutComp *>(buf);
int tempId = obj->TemplateID;
//std::cout<<tempId<<"\n";
switch(tempId)
{
case 10019: //login
{
//const UserLoginResponse *obj = reinterpret_cast<const UserLoginResponse *>(buf);
std::cout<<"*********[SERVER]: LOGIN ACKNOWLEDGEMENT RECEIVED************* "<<"\n";
break;
}
}
std::cout << "RX: " << len << " bytes\n";
if(this->input_type==2)
interact(s);
}
void interact(tcp::socket& s)
{
if(this->input_type == -1){
std::cout<<"what type of input you want ? option 1 : test.json / option 2 : manually through command line :";
int temp;
std::cin>>temp;
this->input_type = temp;
}
if(this->input_type==1)
{
//std::cout<<"reading from file\n";
std::ifstream input_file("test.json");
Json::Reader reader;
Json::Value input;
reader.parse(input_file, input);
for(auto i: input["commands"])
{
std::string str = i["type"].asString();
if(str=="login")
this->login_request(s,i);
}
std::cout<<"File read completely!! \n Do you want to continue or exit?: ";
}
}
The sending works fine, the message is sent and the server responds in a correct manner, but what I need to understand is why is the control not going to on_send_completed (which prints sent x bytes). Neither it prints the message [SERVER]: LOGIN ACKNOWLEDGEMENT RECEIVED, I know I am missing something basic or am doing something wrong, please correct me.
login_request function:
void login_request(tcp::socket& socket,Json::Value o) {
/*Some buffer being filled*/
async_write(socket, boost::asio::buffer(&info, sizeof(info)), on_send_completed);
}
Thanks in advance!!
From a cursory scan it looks like you redefined buffer_ that was already a class member (of IO, presumably).
It's hidden by the local in start_read, which is both UB (because the lifetime ends before the async read operation completes) and also makes it so the member _buffer isn't used.
I see a LOT of confusing code though. Why are you doing synchronous reads from within completion handlers?
I think you might be looking for the composed-ooperation reads (boost::asio::async_read and boost::asio::async_until)

Can I Guarantee that MoveFile() will not Lose a File if it Fails?

Given a CRITICAL file named:
C:\Lvl1\test.file
that I want to move to:
C:\Lvl1\Lvl2\test.file
Using the ::MoveFile() function, can I rely on MoveFile() to not lose the file data if the function call fails?
The code to execute this would probably be something like:
int main()
{
bool bMoveFileFailed = false;
// Please just accept that a "CalcCRC32()" exists and returns the file CRC32!
uint crcOriginal = CalcCRC32("C:\\Lvl1\\test.file");
if( FALSE == ::MoveFile("C:\\Lvl1\\test.file", "C:\\Lvl1\\Lvl2\\test.file") )
{
bMoveFileFailed = false;
}
std::cout << "MoveFile() " << (bMoveFileFailed ? "FAILED" : "SUCCEEDED") << std::endl;
uint crcNew = CalcCRC32(bMoveFileFailed ? "C:\\Lvl1\\test.file" : "C:\\Lvl1\\Lvl2\\test.file");
if ( crcNew == crcOriginal )
{
std::cout << "The File Integrity was maintained!" << std::endl;
}
else
{
std::cout << "The file was corrupted or lost!" << std::endl;
}
return 0;
}

Use of a WebSocket++ server and client in the same function/program

My question is how to set up a WebSocket++ server and create a WebSocket++ client that connects to this server in the same program or function? (for test purpose)
Details:
I would like to use library WebSocket++ in my C++ program to stream data on a websocket. I have a websocket client that sends data to an extern websocket server.
As a good programmer, I try to write some tests to check everything is fine. Therefore I want to setup a WebSocket++ server to test the data I send from the WebSocket++ client.
From the examples, I have managed to create a server in a program and a client in another program. It works like a charm. Problem arises when I try to put the server and the client code in the same program (code is given below): The client can not connect to server, and leads to a timeout handshake.
I guess it is an ASIO problem or a thread problem, but I have no idea how to deal with it.
From the classical example I met, I had to replace echo_server.start() with echo_server.poll(), to have a non stop blocking process. It is not blocking but it prevents the client from connecting to server.
Any advise on how to solve this would be of great help!!
Should I use thread or anything else?
Below is the program I try to get running, where I want the client to connect to the server.
It is based on the merge of tutorials found here and here
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>
#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg)
{
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
typedef websocketpp::client<websocketpp::config::asio_client> client;
class connection_metadata {
public:
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
: m_id(id)
, m_hdl(hdl)
, m_status("Connecting")
, m_uri(uri)
, m_server("N/A")
, m_error_reason("")
,m_messages()
{}
void on_open(client * c, websocketpp::connection_hdl hdl) {
m_status = "Open";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
}
void on_fail(client * c, websocketpp::connection_hdl hdl) {
m_status = "Failed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
m_error_reason = con->get_ec().message();
}
void on_close(client * c, websocketpp::connection_hdl hdl) {
m_status = "Closed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
std::stringstream s;
s << "close code: " << con->get_remote_close_code() << " ("
<< websocketpp::close::status::get_string(con->get_remote_close_code())
<< "), close reason: " << con->get_remote_close_reason();
m_error_reason = s.str();
}
void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
m_messages.push_back("<< " + msg->get_payload());
} else {
m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
}
}
websocketpp::connection_hdl get_hdl() const {
return m_hdl;
}
int get_id() const {
return m_id;
}
std::string get_status() const {
return m_status;
}
void record_sent_message(std::string message) {
m_messages.push_back(">> " + message);
}
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
private:
int m_id;
websocketpp::connection_hdl m_hdl;
std::string m_status;
std::string m_uri;
std::string m_server;
std::string m_error_reason;
std::vector<std::string> m_messages;
};
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
out << "> URI: " << data.m_uri << "\n"
<< "> Status: " << data.m_status << "\n"
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
out << "> Messages Processed: (" << data.m_messages.size() << ") \n";
std::vector<std::string>::const_iterator it;
for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
out << *it << "\n";
}
return out;
}
class websocket_endpoint {
public:
websocket_endpoint () : m_endpoint(), m_thread(), m_connection_list(), m_next_id(0)
{
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
m_endpoint.init_asio();
m_endpoint.start_perpetual();
m_thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_endpoint);
}
~websocket_endpoint() {
m_endpoint.stop_perpetual();
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
if (it->second->get_status() != "Open") {
// Only close open connections
continue;
}
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
websocketpp::lib::error_code ec;
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
if (ec) {
std::cout << "> Error closing connection " << it->second->get_id() << ": "
<< ec.message() << std::endl;
}
}
m_thread->join();
}
int connect(std::string const & uri) {
websocketpp::lib::error_code ec;
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
if (ec) {
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
return -1;
}
int new_id = m_next_id++;
connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(new_id, con->get_handle(), uri);
m_connection_list[new_id] = metadata_ptr;
con->set_open_handler(websocketpp::lib::bind(
&connection_metadata::on_open,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_fail_handler(websocketpp::lib::bind(
&connection_metadata::on_fail,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_close_handler(websocketpp::lib::bind(
&connection_metadata::on_close,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_message_handler(websocketpp::lib::bind(
&connection_metadata::on_message,
metadata_ptr,
websocketpp::lib::placeholders::_1,
websocketpp::lib::placeholders::_2
));
m_endpoint.connect(con);
return new_id;
}
void close(int id, websocketpp::close::status::value code, std::string reason) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
if (ec) {
std::cout << "> Error initiating close: " << ec.message() << std::endl;
}
}
void send(int id, std::string message) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.send(metadata_it->second->get_hdl(), message, websocketpp::frame::opcode::text, ec);
if (ec) {
std::cout << "> Error sending message: " << ec.message() << std::endl;
return;
}
metadata_it->second->record_sent_message(message);
}
connection_metadata::ptr get_metadata(int id) const {
con_list::const_iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
return connection_metadata::ptr();
} else {
return metadata_it->second;
}
}
private:
typedef std::map<int,connection_metadata::ptr> con_list;
client m_endpoint;
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
con_list m_connection_list;
int m_next_id;
};
int main() {
bool done = false;
std::string input;
websocket_endpoint endpoint;
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.poll();
// echo_server.run();
//thread t(bind(&WSServer::poll,echo_server));
//t.detach();
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "connect <ws uri>\n"
<< "send <connection id> <message>\n"
<< "close <connection id> [<close code:default=1000>] [<close reason>]\n"
<< "show <connection id>\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else if (input.substr(0,7) == "connect") {
int id = endpoint.connect(input.substr(8));
if (id != -1) {
std::cout << "> Created connection with id " << id << std::endl;
}
} else if (input.substr(0,4) == "send") {
std::stringstream ss(input);
std::string cmd;
int id;
std::string message = "";
ss >> cmd >> id;
std::getline(ss,message);
endpoint.send(id, message);
} else if (input.substr(0,5) == "close") {
std::stringstream ss(input);
std::string cmd;
int id;
int close_code = websocketpp::close::status::normal;
std::string reason = "";
ss >> cmd >> id >> close_code;
std::getline(ss,reason);
endpoint.close(id, (websocketpp::close::status::value)close_code, reason);
} else if (input.substr(0,4) == "show") {
int id = atoi(input.substr(5).c_str());
connection_metadata::ptr metadata = endpoint.get_metadata(id);
if (metadata) {
std::cout << *metadata << std::endl;
} else {
std::cout << "> Unknown connection id " << id << std::endl;
}
} else {
std::cout << "> Unrecognized Command" << std::endl;
}
}
return 0;
}
The CMakeLists.txt needed to compile this program looks like this
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
FIND_PACKAGE(Boost 1.53 COMPONENTS random system thread REQUIRED)
IF(Boost_FOUND)
MESSAGE(STATUS "Boost_INCLUDE_DIRS : ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost_LIBRARIES : ${Boost_LIBRARIES}")
ENDIF()
INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(SYSTEM websocketpp)
ADD_EXECUTABLE(DemoWebSocket DemoWebSocket.cpp)
TARGET_LINK_LIBRARIES(DemoWebSocket
${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_RANDOM_LIBRARY})
IF(WIN32)
TARGET_LINK_LIBRARIES(DemoWebSocket wsock32 ws2_32)
ELSE()
TARGET_LINK_LIBRARIES(DemoWebSocket pthread rt)
ENDIF()
The solutions consists in creating a thread that creates a WebSocket server and launches its runnning. Then the client code can be used in the same function.
Below is the code that allows to use a WebSocket++ server and a a WebSocket++ client in the same function/program
void createServerEcho();
void createServerEcho()
{
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
}
int main()
{
websocket_endpoint endpoint;
std::thread serverThread (createServerEcho);
/*
* Client code part with variable endpoint, with creation, connection, ...
*/
serverThread.join();
}