boost::asio doesn't work - boost

With the following class
the header:
namespace msgSrv {
class endPoint {
public:
asio::ip::udp::endpoint ep;
endPoint(std::string ip, int port);
};
class msgSrv {
private:
asio::ip::udp::socket *asioSocket;
asio::io_service *asioIoService;
int listenPort;
boost::array<char, 1> rcvBuff;
asio::ip::udp::endpoint lastRcvdPcktEndp;
char * sbuff;
public:
boost::condition_variable cond;
boost::mutex mut;
msgSrv(int listenPort);
virtual ~msgSrv();
void start();
void pckRcvd(const asio::error_code& error, std::size_t bytes_transferred);
void sendTo(const char* buff, int len, endPoint ep);
void sendHnd(const asio::error_code& error, std::size_t bytes_transferred);
};
}
the .cpp
#include "msgSrv.h"
namespace msgSrv {
endPoint::endPoint(const std::string ip, int port) {
asio::ip::address addr = asio::ip::address::from_string(ip);
ep = asio::ip::udp::endpoint(addr, port);
}
msgSrv::msgSrv(int listenPort) {
// TODO Auto-generated constructor stub
this->listenPort = listenPort;
try {
asioIoService = new asio::io_service();
asioSocket = new asio::ip::udp::socket(*asioIoService,
asio::ip::udp::endpoint(asio::ip::udp::v4(), listenPort)); //new asio::ip::udp::socket_(*asioIoService, udp::endpoint(udp::v4(), listenPort));
} catch (std::exception &e) {
std::cerr << "Error initializing ioservice or socket:" << e.what();
}
asioIoService->run();
}
msgSrv::~msgSrv() {
// TODO Auto-generated destructor stub
delete asioIoService;
delete asioSocket;
}
void msgSrv::start() {
asioSocket->async_receive_from(asio::buffer(rcvBuff), lastRcvdPcktEndp,
boost::bind(&msgSrv::pckRcvd, this, asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void msgSrv::pckRcvd(const asio::error_code& error,
std::size_t bytes_transferred) {
std::cout << "Rcvd! " << lastRcvdPcktEndp.address().to_string() << ":"
<< lastRcvdPcktEndp.port() << "\n";
}
void msgSrv::sendTo(const char* buff, int len, endPoint ep) {
sbuff = new char[len];
mempcpy(sbuff, buff, len);
asioSocket->async_send_to(asio::buffer(sbuff, len), ep.ep, boost::bind(
&msgSrv::sendHnd, this, asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void msgSrv::sendHnd(const asio::error_code& error,
std::size_t bytes_transferred) {
std::cout << "Snt!\n";
delete sbuff;
}
}
and the following "main" file:
int main()
{
msgSrv::msgSrv aa(4450);
aa.start();
msgSrv::endPoint ep("127.0.0.1", 4450);
std::string a("Prova!");
int len = a.length();
aa.sendTo(a.c_str(), len, ep);
std::cout << "sent...\n";
std::cout << "notified...\n";
}
all I get is:
terminate called after throwing an instance of 'asio::system_error'
what(): mutex: Invalid argument
sent...
notified...
What's wrong?? I tried even to put a while(1) in the main, to see if something happens... I even tried to put a condition in the main that is unlocked by the receive handler... all remains locked... So what??? No idea!

I don't see you actually locking any muxtex, so that error is strange.
However your problem is to calling asioIoService->run() inside the constructor, witch fall in infinite loop. The solution is to create a new boost::thread, witch call asioIoService->run() itself. This thread will be processing all jobs. You may also call asio::io_service::run() with more then one thread, to get processing on more then one job at the same time.
m_thread = new boost::thread(boost::bind(&asio::io_service::run,asioIoService));
Calling asioIoService->stop() will force exit of asio::io_service::run(), thus closing the thread. You must join this thread to ensure that thread terminates before destroying asioIoService pointer in the destructor of your msgSrv class.

Related

boost asio io_context.run() segmentation error

I am trying to make simple server that remembers and operates some variables with receive short instructions.
I didn't complete this server, and I am trying to test connecting to the server.
But when I try to connect the server, it occurs segmentation fault.
It seems that be occured at io_context.run() function.
I don't know exact cause of this error in spite of reading asio's reference page.
Please help me..
I think that you don't have to read code of data(data.hpp).
This is server code.
//server.cpp
#include <iostream>
#include "network/sc_network.hpp"
int main(int argc, char *argv[])
{
try
{
if(argc != 2)
{
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[1]));
server server(io_context, endpoint);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
This is client code.
//client.cpp
#include <iostream>
#include <thread>
#include <cstdlib>
#include <boost/asio.hpp>
#include "network/data/data.hpp"
using boost::asio::ip::tcp;
class client{
private:
boost::asio::io_context& io_context_;
tcp::socket socket_;
oper_data *data_;
void do_connect(const tcp::resolver::results_type& endpoints)
{
boost::asio::async_connect(socket_, endpoints,
[this](boost::system::error_code ec, tcp::endpoint)
{
if(!ec)
{
boost::asio::async_read(socket_,
boost::asio::buffer(data_, sizeof(oper_data)),
[this](boost::system::error_code ec, std::size_t)
{
if(!ec)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_,sizeof(oper_data)),
[this](boost::system::error_code ec, std::size_t)
{
});
}
else
{
socket_.close();
}
});
}
else
{
socket_.close();
}
});
}
public:
client(boost::asio::io_context& io_context,
const tcp::resolver::results_type& endpoints)
: io_context_(io_context),
socket_(io_context)
{
do_connect(endpoints);
}
void write(const oper_data& data)
{
boost::asio::post(io_context_,
[this, data]()
{
});
}
};
int main(int argc, char *argv[])
{
try
{
if(argc != 3)
{
std::cerr << "Usage: client <host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(argv[1], argv[2]);
client c(io_context, endpoints);
std::thread t([&io_context](){ io_context.run(); });
char line[128];
while (std::cin.getline(line, 128))
{
oper_data data;
//processing the line with deviding in 3 words.
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
this is sc_network.hpp
//sc_network.hpp
#include <boost/asio.hpp>
#include <memory>
#include <utility>
#include "data/data.hpp"
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
private:
tcp::socket socket_;
data_proc data_proc_;
public:
session(tcp::socket socket)
: socket_(std::move(socket)){}
void start()
{
oper_data *input_data;
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, input_data](boost::system::error_code ec, std::size_t)
{
if(!ec)
{
data_proc_.set_data(*input_data);
data_proc_.oper_process();
start();
}
else
{
return;
}
});
}
};
class server
{
private:
tcp::acceptor acceptor_;
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket)
{
if(!ec)
{
session ex_session(std::move(socket));
}
do_accept();
});
}
public:
server(boost::asio::io_context& io_context,
const tcp::endpoint& endpoint)
: acceptor_(io_context, endpoint)
{
do_accept();
}
};
this is data.hpp.
//data.hpp
#include <deque>
#include <cstring>
#include "favdew_utility.hpp"
#define max_oper_size 5
#define max_oper_buf max_oper_size + 1
struct oper_data {
char oper_[max_oper_buf] = "\0";
char *operand_;
char *oper_num_;
};
typedef struct oper_data oper_data;
class data_store {
private:
char *var_name_;
char *var_value_;
public:
data_store()
: var_name_(NULL), var_value_(NULL) {}
data_store(const char *var_name, const char *var_value)
{
std::size_t var_name_size = strlen(var_name) + 1;
var_name_ = new char[var_name_size];
strncpy(var_name_, var_name, strlen(var_name));
std::size_t var_value_size = strlen(var_value) + 1;
var_value_ = new char[var_value_size];
strncpy(var_value_, var_value, strlen(var_value));
}
char *var_name() { return var_name_; }
char *var_value() { return var_value_; }
void set_value(const char *var_value) {
var_value_ = new char[strlen(var_value) + 1];
strncpy(var_value_, var_value, strlen(var_value));
}
};
typedef std::deque<data_store> data_queue;
class data_proc {
private:
oper_data data_;
data_queue proc_queue;
void var()
{
if (data_store *var = this->get_var(data_.operand_)) {
var->set_value(data_.oper_num_);
}
else {
data_store input_data(data_.operand_, data_.oper_num_);
this->proc_queue.push_back(input_data);
}
}
bool sum()
{
data_store *var = this->get_var(data_.operand_);
if ( (var) && isNumber(var->var_value()))
{
const int input_data = std::atoi(var->var_value()) +
std::atoi(this->data_.oper_num_);
var->set_value(std::to_string(input_data).c_str());
return true;
}
else
return false;
}
bool dif()
{
data_store *var = this->get_var(data_.operand_);
if ((var) && isNumber(var->var_value()))
{
const int input_data = std::atoi(var->var_value()) -
std::atoi(this->data_.oper_num_);
var->set_value(std::to_string(input_data).c_str());
return true;
}
else
return false;
}
public:
data_proc()
{
oper_data input_data;
//<input_data.oper_> is already initialized with "\0"
std::memset(input_data.operand_, 0, sizeof(char *));
std::memset(input_data.oper_num_, 0, sizeof(char *));
}
data_proc(const char *oper, const char *operand, const char *oper_num)
{
strncpy(data_.oper_, oper, max_oper_size);
std::size_t operand_size = strlen(operand) + 1;
data_.operand_ = new char[operand_size];
strncpy(data_.operand_, operand, strlen(operand));
std::size_t oper_num_size = strlen(oper_num) + 1;
data_.oper_num_ = new char[oper_num_size];
strncpy(data_.oper_num_, oper_num, strlen(oper_num));
}
inline void set_data(oper_data data)
{
this->data_ = data;
}
void set_data(const char *oper, const char *operand, const char *oper_num)
{
strncpy(data_.oper_, oper, max_oper_size);
std::size_t operand_size = strlen(operand) + 1;
data_.operand_ = new char[operand_size];
strncpy(data_.operand_, operand, strlen(operand));
std::size_t oper_num_size = strlen(oper_num) + 1;
data_.oper_num_ = new char[oper_num_size];
strncpy(data_.oper_num_, oper_num, strlen(oper_num));
}
data_store *get_var(const char *var_name)
{
const std::size_t queue_size = this->proc_queue.size();
for (std::size_t i=0; i < queue_size; i++) {
if (!strcmp(this->proc_queue[i].var_name(), var_name)) {
return &proc_queue[i];
}
}
return NULL;
}
bool oper_process()
{
const char *oper = this->data_.oper_;
if (!strcmp(oper, "var")) {
var();
return true;
}
else if (!strcmp(oper, "sum")) {
sum();
return true;
}
else if (!strcmp(oper, "dif")) {
dif();
return true;
}
else {
return false;
}
}
};
this is favdew_utility.hpp
#include <string>
#include <cstdlib>
bool isNumber(const char *str)
{
std::size_t length = strlen(str);
for (std::size_t i = 0; i < length; i++)
{
if (!('0' < str[i] && str[i] < '9'))
return false;
continue;
}
return true;
}
bool isEmpty(void *buffer)
{
if (!buffer || *(char *)buffer == '\0')
return true;
else
return false;
}
There are many issues, just pointing out a few:
The declaration
session ex_session(std::move(socket));
This creates a local (stack) variable that inherits from enable_shared_from_this. Using shared_from_this will be Undefined Behaviour
Session gets immediately destructed and start() appears to be never called
If session::start() were called, it would fail because it starts an async operation without guarding the lifetime of the session instance:
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, input_data](boost::system::error_code ec, std::size_t) { ....
At the very least you need to capture the shared pointer to the session:
auto self = shared_from_this();
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, self, input_data](boost::system::error_code ec, std::size_t)
Even worse, input_data is never initialized. Again: Undefined Behaviour. Even if you did initialize it, you'd have to manage lifetime; why not make it a member of the session, instead of dynamically allocating (or forgetting to, as you have now)?
Caution: No, you cannot stack-allocate inside start() not even if you capture it in the lambda, because the async operations will not complete before start() exits.
Same in client: data_ is never initialized. Boom.
Even if you had it correctly allocated, using it as an asio::buffer() treats it as a POD.
Since, however, data_proc happily aggregates a data_queue which is std::deque<> it obviously IS NOT POD. More Undefined Behaviour.
What you probably need is to serialize your datastructures, instead of hoping that copying some bytes of memory is going to magically "work". It won't!
See e.g. sending/receiving a struct in boost::asio
Note While you're at is, use C++ instead of C? All the raw pointers and char* are complexity that you don't need, and it is handing your dozens of footguns or ends or rope that you're gonna hurt yourself more with.
In client.cpp you have:
std::thread t([&io_context](){ io_context.run(); });
char line[128];
while (std::cin.getline(line, 128))
{
oper_data data;
//processing the line with deviding in 3 words.
}
Soooo many things...
use std::getline, not std::istream::getline
the thread needs to be joined (https://en.cppreference.com/w/cpp/thread/thread/~thread)
if all you do is block for input, why have the thread?
io_context.run(); // replaces all of the above
data_store is also not POD, but it is also a living memory-leak. All the new-ed memory is never freed.
Note that, the way it's written, the struct might APPEAR to be POD, but logically it isn't (Rule Of Three). Basically, you wrote it in C, not C++. This foregoes all abstractions that C++ has, and now the compiler cannot tell that the struct refers to non-owned resources.
Mind you, this gives me the impression that oper_data might have similar issues (though at first I assumed that operand_ and _oper_num are supposed to point inside the fixed-size buffer oper_[])
Summarizing:
You're way ahead of yourself. Start much simpler. Use C++ (std::string, never use new/delete, actually use std::make_shared if you want to enable_shared_from_this).
You'll be much happier. Feel free to come back with simpler questions when you get stuck, ideally the SSCCE would be a (few) dozen or so lines.

Get notification in Asio if `dispatched` or `post` have finished

I want to know when dispatchhas finished with some specific work
service.dispatch(&some_work);
I want to know this because I need to restart some_work if it has finished.
struct work
{
std::shared_ptr<asio::io_service> io_service;
bool ready;
std::mutex m;
template <class F>
void do_some_work(F&& f)
{
if (io_service && ready) {
m.lock();
ready = false;
m.unlock();
io_service->dispatch([&f, this]() {
f();
m.lock();
ready = true;
m.unlock();
});
}
}
work(std::shared_ptr<asio::io_service> io_service)
: io_service(io_service)
, ready(true)
{
}
};
int
main()
{
auto service = std::make_shared<asio::io_service>();
auto w = std::make_shared<asio::io_service::work>(*service);
std::thread t1([&] { service->run(); });
work some_work{ service };
for (;;) {
some_work.do_some_work([] {
std::cout << "Start long draw on thread: " << std::this_thread::get_id()
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "End long draw on thread: " << std::this_thread::get_id()
<< std::endl;
});
}
w.reset();
t1.join();
}
There are some problems with the code, for example if some_workgoes out of scope, then the running taskwould still write to ready.
I am wondering if something like this already exists in Asio?
For lifetime issues, the common idiom is indeed to use shared pointers, examples:
Ensure no new wait is accepted by boost::deadline_timer unless previous wait is expired
Boost::Asio Async write failed
Other than that, the completion handler is already that event. So you would do:
void my_async_loop() {
auto This = shared_from_this();
socket_.async_read(buffer(m_buffer, ...,
[=,This](error_code ec, size_t transferred) {
if (!ec) {
// do something
my_async_loop();
}
}
);
}
This will re-schedule an (other?) async operation once the previous has completed.
On the subject of threadsafety, see Why do I need strand per connection when using boost::asio?

std::bind not able to call the class member function: C++

I have to implement A valve Open function (for specified duration).
I am using boost::asio::deadline_timer
My class member function to open valve is:
bool Valves::valveOpen(ValveType type)
{
switch (type)
{
case eVentValve:
tblMap_.digitalInput[eVentValveK1].setBit();
if (tblMap_.digitalOutput[eOutK1VentValve].getBit())
{
isVentOpen_ = true;
}
return isVentOpen_;
case eVacuumPumpValve:
....
....
}
Class member function to close the valve is:
bool Valves::valveClose(ValveType type)
{
switch (type)
{
case eVentValve:
tblMap_.digitalInput[eVentValveK1].clearBit();
if (!tblMap_.digitalOutput[eOutK1VentValve].getBit())
{
isVentOpen_ = false;
}
return !isVentOpen_;
case eVacuumPumpValve:
....
....
}
I am trying to achieve the timer action as below
bool Valves::valveTimedOpen(ValveType type, int sec)
{
boost::asio::io_service io;
switch (type)
{
case eVentValve:
{
std::bind(&Valves::valveOpen, this, type); //Here
boost::asio::deadline_timer t(io, boost::posix_time::seconds(sec));
t.async_wait(std::bind(&Valves::valveClose, this, type));
boost::thread th(boost::bind(&boost::asio::io_service::run, &io));
return true;
}
case eVacuumPumpValve:
.....
.....
}
The code hits the line Here i.e.
std::bind(&Valves::valveOpen, this, type); but it does not go to bool Valves::valveOpen(ValveType type) function.
Can someone let me know the issue with this code?
Variables io and t go out of scope as soon as valveTimedOpen exits. You need to rethink the way you interact with the boost asio components e.g. the io_service could be a member of your class, and the timer could be dynamically allocated and needs to be deleted in the completion handler.
Also, keep in mind that if you plan on re-using an io_service object, you also need to reset it before calling run again.
auto fn = std::bind(&Test::Open, shared_from_this(), std::placeholders::_1);
fn(type);
Calls the Open() correctly.
io_service and boost::deadline_timer I have to make class member as suggested by #Ralf
Working Code:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
class Test : public std::enable_shared_from_this <Test>
{
public:
Test() :io(), timer(io){}
void Open(int num);
void Close(int num);
void TimedOpen(int num, int dur);
void Run();
private:
boost::asio::io_service io;
boost::asio::deadline_timer timer;
};
void Test::Open(int num)
{
std::cout << "Open for Number : " << num << std::endl;
}
void Test::Close(int num)
{
std::cout << "Close for Number : " << num << std::endl;
}
void Test::TimedOpen(int num, int dur)
{
io.reset();
auto fn = std::bind(&Test::Open, shared_from_this(), std::placeholders::_1);
fn(num);
timer.expires_from_now( boost::posix_time::seconds(dur));
timer.async_wait(std::bind(&Test::Close, shared_from_this(), num));
Run();
std::cout << "Function Exiting" << std::endl;
}
void Test::Run()
{
boost::thread th(boost::bind(&boost::asio::io_service::run, &io));
}
int main()
{
auto t = std::make_shared<Test>();
t->TimedOpen(5, 5);
char line[128];
while (std::cin.getline(line, 128))
{
if (strcmp(line, "\n")) break;
}
return 0;
}

set_option: Invalid argument when setting option boost::asio::ip::multicast::join_group inside lambda

This code is intended to receive UDP multicast messages using Boost.Asio. A Boost system_error exception is thrown by the code below when the second set_option() call inside receiver's constructor is made (to join the multicast group). The complaint is "Invalid argument". This seems to be related to the fact that the constructor occurs inside a lambda defined inside IO::doIO(), because using a member for the std::thread with identical functionality (IO::threadFunc()) instead results in the expected behavior (no exceptions thrown).
Why is this, and how can I fix it so that I may use a lambda?
//g++ -std=c++11 doesntWork.cc -lboost_system -lpthread
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
class IO
{
public:
class receiver
{
public:
receiver(
boost::asio::io_service &io_service,
const boost::asio::ip::address &multicast_address,
const unsigned short portNumber) : _socket(io_service)
{
const boost::asio::ip::udp::endpoint listen_endpoint(
boost::asio::ip::address::from_string("0.0.0.0"), portNumber);
_socket.open(listen_endpoint.protocol());
_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
_socket.bind(listen_endpoint);
std::cerr << " About to set option join_group" << std::endl;
_socket.set_option(boost::asio::ip::multicast::join_group(
multicast_address));
_socket.async_receive_from(
boost::asio::buffer(_data),
_sender_endpoint,
boost::bind(&receiver::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
void handle_receive_from(
const boost::system::error_code &error,
const size_t bytes_recvd)
{
if (!error)
{
for(const auto &c : _data)
std::cout << c;
std::cout << std::endl;
}
}
private:
boost::asio::ip::udp::socket _socket;
boost::asio::ip::udp::endpoint _sender_endpoint;
std::vector<unsigned char> _data;
}; // receiver class
void doIO()
{
const boost::asio::ip::address multicast_address =
boost::asio::ip::address::from_string("235.0.0.1");
const unsigned short portNumber = 9999;
// _io_service_thread = std::thread(
// &IO::threadFunc, this, multicast_address, portNumber);
_io_service_thread = std::thread([&, this]{
try {
// Construct an asynchronous receiver
receiver r(_io_service, multicast_address, portNumber);
// Now run the IO service
_io_service.run();
}
catch(const boost::system::system_error &e)
{
std::cerr << e.what() << std::endl;
throw e; //std::terminate()
}
});
}
void threadFunc(
const boost::asio::ip::address &multicast_address,
const unsigned short portNumber)
{
try {
// Construct an asynchronous receiver
receiver r(_io_service, multicast_address, portNumber);
// Now run the IO service
_io_service.run();
}
catch(const boost::system::system_error &e)
{
std::cerr << e.what() << std::endl;
throw e; //std::terminate()
}
}
private:
boost::asio::io_service _io_service;
std::thread _io_service_thread;
}; // IO class
int main()
{
IO io;
io.doIO();
std::cout << "IO Service is running" << std::endl;
sleep(9999);
}
There is a race condition that can result in dangling references being accessed, invoking undefined behavior. The lambda capture-list is capturing the automatic variables, multicast_address and portNumber, by reference. However, the lifetime of these objects may end before their usage within _io_service_thread:
void doIO()
{
const boost::asio::ip::address multicast_address = /* ... */;
const unsigned short portNumber = /* ... */;
_io_service_thread = std::thread([&, this] {
// multicast_address and portNumber's lifetime may have already ended.
receiver r(_io_service, multicast_address, portNumber);
// ...
});
} // multicast_address and portNumber are destroyed.
To resolve this, consider capturing by value so that the thread operates on copies whose lifetimes will remain valid until the end of the thread. Change:
std::thread([&, this] { /* ... */ }
to:
std::thread([=] { /* ... */ }
This issue does not present itself when std::thread is constructed with the function and all its arguments, as the std::thread constructor will copy/move all provided arguments into thread-accessible storage.
Also, be aware of the destruction of the _io_service_thread object will invoke std::terminate() if it is still joinable within IO's destructor. To avoid this behavior, consider explicitly joining the _io_service_thread from the main thread.

Using boost::asio::io_service::post()

First i asked this Running a function on the main thread from a boost thread and passing parameters to that function
so now i am trying this:
The following is a console c++ project where i perfectly simulated my big project
TestServicePost.cpp
#include "stdafx.h"
#include "SomeClass.h"
int _tmain(int argc, _TCHAR* argv[])
{
SomeClass* s = new SomeClass();
while(true)
{
s->update();
}
return 0;
}
SomeClass.h
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <queue>
class ServiceNote
{
public:
std::string getType()
{
std::stringstream typeSS;
typeSS << "LamasaTech.MultiWall.PostNote." << (NoteType.compare("Normal") == 0 ? "Node" : "Header") << "." << Shape << "." << Colour;
return typeSS.str();
}
int Action;
int CNoteId;
std::string Colour;
int NoteId;
std::string NoteType;
int SessionId;
std::string Shape;
std::string Style;
std::string Text;
int X;
int Y;
};
class SomeClass
{
public:
SomeClass();
~SomeClass();
void update();
private:
std::queue<ServiceNote> pendingNotes;
void addToQueue(ServiceNote sn);
void pollService(boost::asio::io_service* svc);
int getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId);
boost::thread servicePoller;
};
SomeClass.cpp
#include "stdafx.h"
#include "SomeClass.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio/signal_set.hpp>
#define POLL_SERVICE = 0;
#define POLLING_WAIT_TIME 1000
#define SAVE_SESSION_EVERY 1800000
SomeClass::SomeClass()
{
boost::asio::io_service io_servicePoller;
io_servicePoller.run();
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, &io_servicePoller));
/*boost::asio::io_service io_sessionSaver;
boost::asio::signal_set signalsSaver(io_sessionSaver, SIGINT, SIGTERM);
signalsSaver.async_wait( boost::bind(&boost::asio::io_service::stop, &io_sessionSaver));
sessionSaver = boost::thread(&SomeClass::saveSessionEvery, io_sessionSaver);*/
}
SomeClass::~SomeClass()
{
}
void SomeClass::update()
{
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
void SomeClass::addToQueue(ServiceNote sn)
{
pendingNotes.push(sn);
}
void SomeClass::pollService(boost::asio::io_service* svc)
{
int messageId = 1;
while(true)
{
if(boost::this_thread::interruption_enabled() && boost::this_thread::interruption_requested())
return;
int currentId = messageId;
messageId = getMessage(svc, "49", messageId);
if(currentId == messageId)
boost::this_thread::sleep(boost::posix_time::milliseconds(POLLING_WAIT_TIME));
}
}
int SomeClass::getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId)
{
try
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("mw.rombus.com", "http");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " "/Service.svc/message/" << sessionId << "/" << messageId << " HTTP/1.0\r\n";
request_stream << "Host: " << "mw.rombus.com" << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
//std::cout << "Invalid response\n";
return messageId;
}
if (status_code != 200)
{
//std::cout << "Response returned with status code " << status_code << "\n";
return messageId;
}
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
std::string fullHeader = "";
while (std::getline(response_stream, header) && header != "\r")
fullHeader.append(header).append("\n");
// Write whatever content we already have to output.
std::string fullResponse = "";
if (response.size() > 0)
{
std::stringstream ss;
ss << &response;
fullResponse = ss.str();
try
{
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
ServiceNote sn;
sn.Action = pt.get<int>("Action");
sn.CNoteId = pt.get<int>("CNoteId");
sn.Colour = pt.get<std::string>("Colour");
sn.NoteId = pt.get<int>("NoteId");
sn.NoteType = pt.get<std::string>("NoteType");
sn.SessionId = pt.get<int>("SessionId");
sn.Shape = pt.get<std::string>("Shape");
sn.Style = pt.get<std::string>("Style");
sn.Text = pt.get<std::string>("Text");
sn.X = pt.get<int>("X");
sn.Y = pt.get<int>("Y");
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
//pendingNotes.push(sn);
}
catch (std::exception const& e)
{
std::string test = e.what();
//std::cerr << e.what() << std::endl;
}
messageId++;
}
// Read until EOF, writing data to output as we go.
std::string fullSth = "";
boost::system::error_code error;
while (boost::asio::read(socket, response,
boost::asio::transfer_at_least(1), error))
{
std::ostringstream ss;
ss << &response;
fullSth = ss.str();
}
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
}
catch (std::exception& e)
{
std::string test = e.what();
std::cout << "Exception: " << e.what() << "\n";
}
return messageId;
}
but i get Unhandled exception at 0x771215de in TestServicePost.exe: 0xC0000005: Access violation writing location 0xcccccce4., right after this line executes:
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
I couldn't define io_service as a class member so i can use it in the destructor ~SomeClass(), would appreciate help on that too
If io_service.post is not the best solution for me please recommend something, as you can see i have a constructor, destructor and an update method who is called every tick, i tried using this and the queue alone but it wasn't thread safe, is there an easy thread safe FIFO to use ?
In SomeClass constructor you actually do the following:
Define a local io_service instance.
Call its run() member-function, which returns immediately, because io_service has no work.
Pass an address of the local object to another thread.
This certainly won't work.
Note that io_service::run() is a kind of "message loop", so it should block the calling thread. Don't call it in object constructor.
I figured out how to declare io_service as a class member:
boost::shared_ptr< boost::asio::io_service > io_servicePoller;
and in the constructor i did the following:
SomeClass::SomeClass()
{
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
io_servicePoller = io_service;
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, io_servicePoller));
}
Some cleanup
SomeClass::~SomeClass()
{
servicePoller.interrupt();
io_servicePoller->stop();
servicePoller.join();
}
and in update i called run which adds the stuff into the queue, then reads them in the while loop
void SomeClass::update()
{
io_servicePoller->run();
io_servicePoller->reset();
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
and changed my members signature to void SomeClass::pollService(boost::shared_ptr< boost::asio::io_service > svc)
So what happens is:
The app starts
inits my class
my class makes a service and starts the thread
the thread fetches items from the service
the main thread checks the io service queue and exuted it
then it uses the queue
Thanks to Igor R. i couldn't have done it without him
and also http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=4 where i got how to make the shared pointer

Resources