Boost::Asio - Passing socket to second class - c++

I am currently trying to get the following application to work:
Await incoming client connection.
Start async. timer in another class.
While the timer runs repeatedly, do other stuff such as async_read and async_write.
Current source code:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "TimerClass.hpp"
using namespace boost::asio;
using namespace boost::asio::ip;
TimerClass *timerClass;
void acceptHandler(const boost::system::error_code &errorCode, tcp::socket *socket) {
timerClass = new TimerClass(socket);
timerClass->startTimer();
while(true) {
// Do other suff such as async_write, ...
}
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &socket));
ioService.run();
return EXIT_SUCCESS;
}
TimerClass.hpp:
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::posix_time;
class TimerClass {
public:
TimerClass(ip::tcp::socket *socket);
void startTimer();
void timerHandler(const boost::system::error_code& errorCode);
deadline_timer timer;
};
TimerClass.cpp:
#include <boost/bind.hpp>
#include "TimerClass.hpp"
TimerClass::TimerClass(ip::tcp::socket *socket) : timer(socket->get_io_service(), boost::posix_time::seconds(1)) {}
void TimerClass::startTimer() {
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
void TimerClass::timerHandler(const boost::system::error_code& errorCode) {
timer.expires_at(timer.expires_at() + boost::posix_time::seconds(1));
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
Handler Tracking Output:
#asio|1461070492.111630|0*1|socket#000000000021FBD0.async_accept
#asio|1461070498.527997|>1|ec=system:0
Questions:
Why won't it even call async_wait in startTimer? Debugging shows that startTimer gets called but I can't find anything in the Handler Tracking output. Why is that?
Am I correctly passing the socket to the TimerClass?
Without the infinite while(true) loop in the acceptHandler the acceptHandler returns but the application crashes before the io_service properly returns. How is that?

I compiled your code and it works for me (using boost version 1.54).
With your code I get the following output:
#asio|1461081908.437388|0*1|socket#003BFE2C.async_accept
#asio|1461081983.220840|>1|ec=system:0
#asio|1461081983.221817|1*2|deadline_timer#001C1318.async_wait
To make it run properly I had to remove the while(true) on your acceptHandler, obtaining the following output (added a std::cout inside the handler):
#asio|1461083707.104424|0*1|socket#0030FB6C.async_accept
#asio|1461083709.061824|>1|ec=system:0
#asio|1461083709.062803|1*2|deadline_timer#00641318.async_wait
#asio|1461083709.062803|<28158494073611763|
#asio|1461083710.064992|>2|ec=system:0
#asio|1461083710.064992|2|deadline_timer#00641318.cancel
#asio|1461083710.064992|2*3|deadline_timer#00641318.async_wait
TimerHandler executed...
#asio|1461083710.065971|<28169626628843099|
#asio|1461083711.065223|>3|ec=system:0
#asio|1461083711.065223|3|deadline_timer#00641318.cancel
#asio|1461083711.065223|3*4|deadline_timer#00641318.async_wait
TimerHandler executed...
I actually did this test using only the header TimerClass.hpp (defining the methods directly within it -I was lazy-) and it worked like a charm, the problem seems to be when using the .cpp file, that's why I asked if you were using include guards (not the issue though, already tested).
You should consider changing your design approach though, i.e. do not use blocking loops in you handlers, just call another asynchronous operation if needed (like async_read or async_write).
Take a look at this question and corresponding accepted answer for a nice server implementation idea. Or try to adapt some of the boost examples to your needs.
As per the segmentation fault you get when separating declaration from definition in the corresponding header and implementation files, you might want to check this other question.

Related

Websocket++ websocket server class member function not recognised from ISR

I'm using an Odroid (Raspi-like ARM Board) to run a small SPI-based radio chip that sends data to, among other things, a Websocket. This is applied using Websocket++: https://github.com/zaphoyd/websocketpp. I've bastardised one of the simple examples, which sends a server message to all clients. The program has a count_server class which handles the websocket, but also has an ISR handled by WiringPi which calls nested functions to handle different operations.
The issue I've got is that in order to send this message, the sending function must be in the count_server class as far as I can tell, to access client addresses etc. This class method is not accesible from inside my ISR, which handles all data received from the radio, so when I try to send a websocket message from inside the ISR I get the error:
error: 'webSocketServer' was not declared in this scope webSocketServer.sendLiveData();
The webSocketServer is an instance of the class count_server, instantiated in main(). Why can't the ISR 'see' the webSocketServer class.
One workaround is to poll inside the count() function, but this blocks the CPU and I'd rather leave it ready to perform other tasks.]
Here's the simplest example I could produce.
Requires Websocket++ and WiringPi for the attachInterrupt.
#include <cstdlib>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <string>
#include <RF24/RF24.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//Websocket++ includes
#include <mutex>
#include <set>
#include <thread>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
//*/
//websocket++ declarations
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
class count_server {
public:
count_server() : m_count(0) {
m_server.init_asio();
m_server.set_open_handler(bind(&count_server::on_open,this,_1));
m_server.set_close_handler(bind(&count_server::on_close,this,_1));
}
void on_open(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.erase(hdl);
}
void sendLiveData(){
std::stringstream ss;
ss << "foobar";
for (auto it : m_connections) {
m_server.send(it,ss.str(),websocketpp::frame::opcode::text);
}
}
void count() {//simple loop thread, most likely not needed, but in working example so lef
t for time being
while (1) {
sleep(1000);
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
int m_count;
server m_server;
con_list m_connections;
std::mutex m_mutex;
};
/****************** Raspberry Pi ***********************/
int interruptPin = 6; // GPIO pin for interrupts - interrupts have been edited to be handled
//by wiringPi, so #6 is used, not #103, check RF24/utility/SPIDEV/interrupt.c for info
int i=0;
/**************************************************************/
void addLiveData(){ //Live data buffer handler + calls sending function when buffer hits max
webSocketServer.sendLiveData();
}
void intHandler(){//when radio chip IRQ goes Low, something happened, this handles it
addLiveData();//recvd = 2;//flag that the data is live data
}
int main(){
attachInterrupt(interruptPin, INT_EDGE_FALLING, &intHandler); //Attach interrupt to bcm p
in 23
count_server webSocketServer;
std::thread t(std::bind(&count_server::count,&webSocketServer));
webSocketServer.sendLiveData();
webSocketServer.run(8080);
}
'

Make boost http client

I am new to boost library so my question is probably not the first one in this forum but I couldn't find a similar case.
Currently I am trying to implement a simple HTTP client which calls REST API.
I inspired my self from the example given on the boost's web site: HTTP client with boost
The example is clear enough for a newbie like me but I would like to make the client to be able to execute multiple requests one by one because the example is a one shot: the client sends a GET request to the server, than it receives the response and after that the io_service.run() returns.
So my question is what I need to use from boost in way to make my client always waiting for new requests to send.
I read something about a io_service::work but I am not sure if it is the right way.
Does anybody have done something similar to the client I am trying to make?
Thanks in advance !
Best regard,
Anton
I do not know if asynchronous version is a must, so I would recommend you to give a try to synchronous version, since it's easier to follow the execution path:
/*
Compile with
g++ -lpthread -lboost_system -lboost_thread -ohttp http.cpp
*/
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
using std::cout;
using std::endl;
using std::vector;
using std::string;
using boost::asio::ip::tcp;
using boost::asio::ip::address;
using boost::asio::io_service;
using boost::asio::buffer;
using boost::system::error_code;
using boost::system::system_error;
int main()
{
try
{
unsigned int PORT = 80;
const string HOST = "216.58.214.238";
const string HTTP_REQUEST = "GET /index.html HTTP/1.0\n\n";
io_service ios;
tcp::endpoint ip_port(address::from_string(HOST), PORT);
while (true)
{
tcp::socket client(ios);
client.connect(ip_port);
const int BUFLEN = 1024;
vector<char> buf(BUFLEN);
client.send(buffer(HTTP_REQUEST, HTTP_REQUEST.size()));
error_code error;
int len = client.receive(buffer(buf, BUFLEN), 0, error);
cout << "main(): buf.data()=";
cout.write(buf.data(), len);
cout << endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
}
}
catch (system_error& exc)
{
cout << "main(): exc.what()=" << exc.what() << endl;
}
return EXIT_SUCCESS;
}
The socket is created each time within the loop because Google (it's IP address is used) closes the connection after each request (status 302 is returned).
In some other cases, HTTP connection does not have to be closed by a server, so socket can be reused.

Boost::Asio - async_wait in async_accept handler crashes application

I am currently trying to create a server application using Boost::Asio that does two simple things:
Accept a client's incoming connection
Once the client has been accepted, start a boost::asio::deadline_timer which repeats itself
The following code shows my current attempt:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::asio::ip;
void timerHandler(const boost::system::error_code& errorCode, deadline_timer* timer) {
timer->expires_at(timer->expires_at() + boost::posix_time::seconds(1));
timer->async_wait(boost::bind(timerHandler, _1, timer));
}
void acceptHandler(const boost::system::error_code &errorCode, io_service *ioService) {
deadline_timer timer(*ioService, boost::posix_time::seconds(1));
timer.async_wait(boost::bind(timerHandler, _1, &timer));
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &ioService));
ioService.run();
return EXIT_SUCCESS;
}
Problem:
The timer somehow does not work as expected in the acceptHandler. Somehow it gets cancelled twice, triggers an error on top of that and eventually crashes the entire application.
Handler Tracking Output:
#asio|1460922050.075890|0*1|socket#000000000015FAD0.async_accept
#asio|1460922051.153952|>1|ec=system:0
#asio|1460922051.153952|1*2|deadline_timer#000000000015F608.async_wait
#asio|1460922051.153952|1|deadline_timer#000000000015F608.cancel
#asio|1460922051.153952|<1|
#asio|1460922051.153952|>2|ec=system:995
#asio|1460922051.153952|2|deadline_timer#000000000015F608.cancel
Questions:
What causes the acceptHandler to cancel the deadline_timer in line 4 of the Handler Tracking output?
What casues the error 995 in line 6 of the Handler Tracking output? Error message is: The I/O operation has been aborted because of either a thread exit or an application request
What causes the timerHandler to cancel the deadline_timer in line 7 of the Handler Tracking output?
timer is allocated on the stack in the acceptHandler and is therefore not valid by the time timerHandler is called. You need to allocate the timer dynamically.
Also, you should check for error codes in both handlers. This is especially important when you want to end the program and cancel the timer.

Stop boost::io_service after certain amount of time

I have a boost::asio::io_service which is doing some work. Now I want to stop this service after a certain amount of time. My first approach was by using boost::thread(io_service.run()), but then I get errors.
Are there other ways to stop io_service?
Thank you!
You can use a deadline_timer.
You can also run the service on another thread like you tried:
boost::thread t = boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_service));
// sometime
io_service.stop(); // io_service is threadsafe
t.join();
Here's a deadline_timer example complete in C++03: Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <iostream>
using namespace boost::asio;
using namespace boost;
io_service svc;
deadline_timer timer(svc);
void work()
{
this_thread::sleep_for(chrono::milliseconds(100));
std::cout << "Work done, rescheduling\n";
svc.post(work);
}
void expiration_handler(system::error_code ec)
{
if (ec != error::operation_aborted)
svc.stop();
}
int main()
{
svc.post(work);
timer.expires_from_now(posix_time::seconds(2));
timer.async_wait(expiration_handler);
svc.run();
}
This prints
Work done, rescheduling
until the deadline is reached after 2 seconds
std::this_thread::sleep_for(std::chrono::seconds(10));
io_service.stop();
Isn't it?
Use deadline_timer.
boost::asio::deadline_timer stop_timer(io_service);
...
// If require stopping
stop_timer.expires_from_now(boost::posix_time::seconds(10));
stop_timer.async_wait(
[&io_service](const boost::system::error_code &ec)
{
io_service.stop();
});

Async Resolve with Boost.Asio

I'm trying to asynchronously resolve a ftp host using Boost.Asio.
Here's what I've tried so far:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class FtpSession {
public:
void Connect(std::string& host) {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(host, "ftp");
resolver.async_resolve(query,
boost::bind(&FtpSession::OnResolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
private:
void OnResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator) {
if (!err)
{
std::cout << "resolved!";
}
else
{
std::cout << "error.";
}
}
};
int main() {
FtpSession session;
std::string host("ftp.remotesensing.org");
session.Connect(host);
return 0;
}
But for some reason, when I execute it, it just doesn't print anything:
alon#alon-GA-73PVM-S2H:~/Desktop$ g++ -o test -lboost_system test.cc
alon#alon-GA-73PVM-S2H:~/Desktop$ ./test
alon#alon-GA-73PVM-S2H:~/Desktop$
No errors or warnings at the compilation though.
How can I fix this?
You need to call io_service.run() to actually do the work methinks. Think of the async_resolve as a request in a request queue - you need something (the io_service) to process the requests in the queue, and to do that, you actually need to run() it! In this case, it will see one request, execute it, call the handler and then exit.
Your io_service and ip::tcp::resolver object are going out of scope. Move both of them into members of the FtpSession class, then invoke io_service::run inside of main after session.Connect(host) to start the event loop.
I answered a similar question a few days ago that may help you as well.