I am using boost::process::child and boost::process::async_pipe to start an application and read asynchronously (through the means of boost::asio) everything that app outputs on screen whenever this happens.
I want to check also if the application is alive by using child::running() method; if not running I'd like to read the exit code using child::exit_code.
This is very useful ESPECIALLY as it is a way to be notified about an application crashing or exiting unexpectedly (I could not find a better way); when the app exits the callback is called with boost::system::error_code set.
Do you know if I can use these two methods inside the callback called by async_pipe::async_read_some ?
In general the much more simple question would be if child::running() and child::exit_code() are thread safe (in both Windows and Linux).
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_pipe_read(
const boost::system::error_code &ec,
std::size_t bytes_transferred
)
{
// Q: Is this call possible? 'handle_pipe_read' can run in any thread
if(c->running())
std::cout << "I am alive" << std::endl;
else
std::cout << "EXIT CODE:" << c->exit_code() << std::endl;
if(ec) return; //app probably exit
// Do something with buffer and re-schedule
schedule_read();
}
int main() {
bp::child c("my_program_url", bp::std_out > in_pipe);
any_c = &c;
schedule_read();
io.run();
}
Since you only run the io_service::run() on the main thread, all completion handlers also run there. There's no threading.
Remember to pass the io_service to the child, and use the on_exit handler:
Live On Coliru
#include <boost/process.hpp>
//#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <system_error>
#include <utility>
#include <iostream>
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read, _1, _2));
}
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred) {
if (ec)
return; // app probably exit
// Do something with buffer and re-schedule
std::cout.write(my_buffer, bytes_transferred);
if (in_pipe.is_open())
schedule_read();
}
int main() {
bp::child c("/bin/ls", bp::std_out > in_pipe,
bp::on_exit([](int code, std::error_code ec) {
std::cout << "Child exited (" << code << "): " << ec.message() << std::endl;
in_pipe.close();
}), io);
schedule_read();
io.run();
std::cout << "Service done (" << c.exit_code() << ")" << std::endl;
}
Prints:
a.out
main.cpp
Child exited (0): Success
Service done (0)
The only solution that worked for me is the following
schedule a completion read
always check calling child::running()
on error_code set don't reschedule
When the pipe gets broken (because of a crash) the completion handler for a read has the boost::error_code argument set to true. Despite this I've seen cases where child::running() is false also when boost::error_code is NOT set.
Related
I recently met a problem with boost::asio asynchronous tasks. I want to return a pointer on an object listening to a port.
It works when I use the socket.read_some method but this method blocks my main and I want my MyClass::create method to return.
So I tried a async_read call but I saw that inside my read() method, no asynchronous tasks are launched. I tried to figure out what may cause the problem but see no solution to this issue.
Here is my code, here it's not with an async_read but with an async_wait, and the same problem appears, the timer is not launched.
Thanks for any help I might get.
The header file:
#ifndef MYCLASS_HPP
#define MYCLASS_HPP
#include <memory>
#include <boost/asio.hpp>
class MyClass
{
public:
MyClass(boost::asio::io_service& ios);
void read();
void read_handler(const boost::system::error_code& error);
static std::shared_ptr<MyClass> create(std:: string const & host, uint16_t port);
bool connect (std::string const & host, uint16_t port);
void connect_handler(const boost::system::error_code& error);
boost::asio::ip::tcp::socket m_socket;
bool m_flag;
std::vector<uint8_t> m_buffer;
};
#endif
Source file:
#include "MyClass.hpp"
#include <boost/bind.hpp>
MyClass::MyClass(boost::asio::io_service& ios)
:m_flag(false), m_socket(ios), m_buffer(20)
{
}
void MyClass::read_handler(const boost::system::error_code& er)
{
std::cout << "Timer waited 5 sec" << std::endl;
}
void MyClass::read()
{
boost::asio::deadline_timer t(m_socket.get_io_service(),boost::posix_time::seconds(5));
t.async_wait(boost::bind(&MyClass::read_handler,this,boost::asio::placeholders::error));
m_socket.get_io_service().run();//Should make the io_service wait for all asynchronous tasks to finish
std::cout << "This message should be displayed after the wait" << std::endl;
}
void MyClass::connect_handler(const boost::system::error_code& error)
{
if(!error)
{
std::cout << "Connection done" << std::endl;
m_flag = 1;
}
else
{
std::cout << "Error in connection: " << error.message() << std::endl;
}
}
//connect method
bool MyClass::connect(std::string const & host, uint16_t port)
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(host),port);
m_socket.async_connect(endpoint,
boost::bind(&MyClass::connect_handler, this,
boost::asio::placeholders::error));
m_socket.get_io_service().run();//Wait async_connect and connect_handler to finish
if (m_flag == 0) return false;
else return true;
}
std::shared_ptr<MyClass> MyClass::create(std:: string const & host, uint16_t port)
{
boost::asio::io_service ios;
std::shared_ptr<MyClass> ptr(new MyClass(ios));
bool bol = ptr->connect(host, port);
ptr->read();
//while(1){}
if(bol == true)
{
//connection success, reading currently listening, pointer is returned to the user
return ptr;
}
else
{
//connection failure, pointer is still returned to the user but not listening as he's not connected
return ptr;
}
}
And my main:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>
#include "MyClass.hpp"
int main()
{
try
{
std::cout << "Creation of instance" << std::endl;
std::shared_ptr <MyClass> var = MyClass::create("127.0.0.1", 8301);
std::cout << "Instance created" << std::endl;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I figured out how to solve my problem.
I had indeed problems with io_service being destroyed after "create" method, so the pointer returned in the main was not able to continue reading.
I had to call run() at one point to launch callbacks but i couldn't do it in the main, as I wanted the main to keep doing other things.
So I created a class launching a separated thread and containing an io_service. That thread is calling run() periodically. It was then added as an attribute to MyClass.
Now I have the call to "create" returning a pointer to MyClass who doesn't stop whatever asynchronous task was launched in MyClass.
I'm trying to build an application that reads from the ftrace pipes at the debug fs.
It seems that when trying to read asynchronously from trace_pipe or
trace_pipe_raw using boost::asio API, the events waiting in pipe are being
processed and printed to screen by the async_read handle, but new events that arrive after the program started don't trigger the async_read handle.
Running the sample code below, i'm getting a print of all events waiting in queue but i don't get any print for new events that arrive later.
The same sample works perfectly if i'm trying to read from manually created pipe using mkfifo but doesn't work for the ftrace pipes.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <string>
#include <iostream>
namespace asio = boost::asio;
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
typedef asio::posix::stream_descriptor stream_descriptor;
#endif
class PipeReader
{
typedef std::shared_ptr<PipeReader> PipeReaderPtr;
typedef std::weak_ptr<PipeReader> PipeReaderWeakPtr;
public:
static PipeReaderWeakPtr Create(asio::io_service& io_service, const std::string& path);
void HandleRead(PipeReaderPtr me, const boost::system::error_code &error);
private:
PipeReader(asio::io_service& io_service, const std::string& path);
stream_descriptor m_pipe;
char buf[4096];
};
PipeReader::PipeReaderWeakPtr PipeReader::Create(asio::io_service& io_service, const std::string& path)
{
PipeReaderPtr ptr(new PipeReader(io_service, path));
ptr->m_pipe.async_read_some(boost::asio::buffer(ptr->buf),
boost::bind(&PipeReader::HandleRead,
ptr.get(),
ptr,
asio::placeholders::error));
return ptr;
}
PipeReader::PipeReader(asio::io_service& io_service, const std::string& path)
: m_pipe(io_service)
{
int dev = open(path.c_str(), O_RDWR);
if (dev == -1) {
std::cout << "failed to open path - " << path << std::endl;
}
else
{
m_pipe.assign(dev);
}
}
void PipeReader::HandleRead(PipeReaderPtr me, const boost::system::error_code &error)
{
if (!error) {
std::string str(me->buf);
std::cout << "got message: " << str << std::endl;
m_pipe.async_read_some(boost::asio::buffer(me->buf),
boost::bind(&PipeReader::HandleRead,
this,
me,
asio::placeholders::error));
}
else
{
std::cout << "got error - " << error.message() << std::endl;
}
}
int main()
{
boost::asio::io_service io_service;
boost::asio::io_service::work dummy(io_service);
PipeReader::Create(io_service, "/sys/kernel/debug/tracing/trace_pipe");
io_service.run();
return 0;
}
I found the problem. it was a bug in the implementation of ftrace that caused the epoll to hang.
The bug was fixed at kernel 3.16.
correspondence thread,
commit in git hub
I am writing some binary data to a device fie like /dev/itun.
void ahaConnector::asyncWriteData(vector<uint8_t> packedMessage) {
cout<<"\n async write data packed message";
deviceStreamDescriptor.assign(device);
boost::asio::write (
deviceStreamDescriptor,
boost::asio::buffer(packedMessage)
);
readbuffer.resize(1024);
deviceStreamDescriptor.async_read_some(boost::asio::buffer(readbuffer),
boost::bind(&ahaConnector::readHeader, this,
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()
));
io_service.run();
}
void ahaConnector::readHeader(const boost::system::error_code &ec, std::size_t bytes_transferred) {
if(!ec) {
std::cout<<"\n Bytes transfereed :"<<bytes_transferred<<" "<<readbuffer.size();
deviceStreamDescriptor.async_read_some(boost::asio::buffer(readbuffer),
boost::bind(&ahaConnector::readHeader, this,
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()
));
Callbacks callbacks;
callbacks.OnReceivingPackedMessage();
io_service.run();
}
else {
cout<<"\n System Error Code "<<ec;
}
}
The callback function readhandler is getting executed successfully, however I am not able to transfer the control from my Callback function to another class.
Is something wrong from the design perspective. I need to handle the message received from the callback function for further logic. Should I use another thread here ?
Looking at this code you might just want to replace the read(device,...) by boost Asio's support for Posix streams:
#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/function.hpp>
#include <iostream>
static int device = 0;
using namespace boost;
int main() {
boost::asio::io_service io_svc;
boost::asio::posix::stream_descriptor iodevice(io_svc, device);
char buffer[1024];
function<void(system::error_code const&, size_t)> callback;
callback = [&](boost::system::error_code const& ec, size_t bytes_transferred) {
if (ec)
{
std::cout << "Error '" << ec.message() << "' during asynchronous operation\n";
}
else
{
std::cout << "Read exactly " << bytes_transferred << " bytes\n";
std::cout << "Data: '";
std::cout.write(buffer, bytes_transferred);
std::cout << "'\n";
iodevice.async_read_some(asio::buffer(buffer), callback);
}
};
iodevice.async_read_some(asio::buffer(buffer), callback);
io_svc.run();
}
See it Live On Coliru.
Sadly on Coliru it can't work because input is redirected from a non-stream. But if you run it interactively it will work and print the first 10 characters entered.
The answer depends on exactly what are the properties of the device. Check the documentation for the device driver you're trying to use. If the device supports non-blocking I/O, open the device with O_NONBLOCK, and use poll() to wait for device to be available for reading or writing.
If the device does not support non-blocking I/O, the only viable option would be to use a separate thread to read and/or write to the device, and use the background thread to construct facade that pretends and behaves like a non/blocking data source and sink.
I have been reading some Boost ASIO tutorials. So far, my understanding is that the entire send and receive is a loop that can be iterated only once. Please have a look at the following simple code:
client.cpp:
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <string>
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
boost::array<char, 4096> buffer;
void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
if (!ec)
{
std::cout << std::string(buffer.data(), bytes_transferred) << std::endl;
sock.async_read_some(boost::asio::buffer(buffer), read_handler);
}
}
void connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
sock.async_read_some(boost::asio::buffer(buffer), read_handler);
}
}
void resolve_handler(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it)
{
if (!ec)
{
sock.async_connect(*it, connect_handler);
}
}
int main()
{
boost::asio::ip::tcp::resolver::query query("localhost", "2013");
resolver.async_resolve(query, resolve_handler);
io_service.run();
}
the program resolves an address, connects to server and reads the data, and finally ends when there is no data.
My question: How can i continue this loop? I mean, How can I keep this connection between a client and server during the entire lifetime of my application so that the server sends data whenever it has something to send?
I tried to break this circle but everything seams trapped inside io_service.run()
Same question holds in case of the my sever also:
server.cpp :
#include <boost/asio.hpp>
#include <string>
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
boost::asio::ip::tcp::socket sock(io_service);
std::string data = "Hello, world!";
void write_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
}
void accept_handler(const boost::system::error_code &ec)
{
if (!ec)
{
boost::asio::async_write(sock, boost::asio::buffer(data), write_handler);
}
}
int main()
{
acceptor.listen();
acceptor.async_accept(sock, accept_handler);
io_service.run();
}
This is just an example. In a real application, I may like to keep the socket open and reuse it for other data exchanges(both read and write). How may I do that.
I value your kind comments. If you have references to some easy solutions addressing this issue, I appreciate if you mention it.
Thank you
Update (server sample code)
Based on the answer given below(update 2), I wrote the server code. Please note that the code is simplified (able to compile&run though). Also note that the io_service will never return coz it is always is waiting for a new connection. And that is how the io_service.run never returns and runs for ever. whenever you want io_service.run to return, just make the acceptor not to accept anymore. please do this in one of the many ways that i don't currently remember.(seriously, how do we do that in a clean way? :) )
enjoy:
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <string>
#include <iostream>
#include <vector>
#include <time.h>
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
//boost::asio::ip::tcp::socket sock(io_service);
std::string data = "Hello, world!";
class Observer;
std::vector<Observer*> observers;
class Observer
{
public:
Observer(boost::asio::ip::tcp::socket *socket_):socket_obs(socket_){}
void notify(std::string data)
{
std::cout << "notify called data[" << data << "]" << std::endl;
boost::asio::async_write(*socket_obs, boost::asio::buffer(data) , boost::bind(&Observer::write_handler, this,boost::asio::placeholders::error));
}
void write_handler(const boost::system::error_code &ec)
{
if (!ec) //no error: done, just wait for the next notification
return;
socket_obs->close(); //client will get error and exit its read_handler
observers.erase(std::find(observers.begin(), observers.end(),this));
std::cout << "Observer::write_handler returns as nothing was written" << std::endl;
}
private:
boost::asio::ip::tcp::socket *socket_obs;
};
class server
{
public:
void CreatSocketAndAccept()
{
socket_ = new boost::asio::ip::tcp::socket(io_service);
observers.push_back(new Observer(socket_));
acceptor.async_accept(*socket_,boost::bind(&server::handle_accept, this,boost::asio::placeholders::error));
}
server(boost::asio::io_service& io_service)
{
acceptor.listen();
CreatSocketAndAccept();
}
void handle_accept(const boost::system::error_code& e)
{
CreatSocketAndAccept();
}
private:
boost::asio::ip::tcp::socket *socket_;
};
class Agent
{
public:
void update(std::string data)
{
if(!observers.empty())
{
// std::cout << "calling notify data[" << data << "]" << std::endl;
observers[0]->notify(data);
}
}
};
Agent agent;
void AgentSim()
{
int i = 0;
sleep(10);//wait for me to start client
while(i++ < 10)
{
std::ostringstream out("");
out << data << i ;
// std::cout << "calling update data[" << out.str() << "]" << std::endl;
agent.update(out.str());
sleep(1);
}
}
void run()
{
io_service.run();
std::cout << "io_service returned" << std::endl;
}
int main()
{
server server_(io_service);
boost::thread thread_1(AgentSim);
boost::thread thread_2(run);
thread_2.join();
thread_1.join();
}
You can simplify the logic of asio based porgrams like follows: each function that calls an async_X function provides a handler. This is a bit like transitions between states of a state machine, where the handlers are the states and the async-calls are transitions between states. Just exiting a handler without calling a async_* function is like a transition to an end state. Everything the program "does" (sending data, receiving data, connecting sockets etc.) occurs during the transitions.
If you see it that way, your client looks like this (only "good path", i.e. without errors):
<start> --(resolve)----> resolve_handler
resolve_handler --(connect)----> connect_handler
connect_handler --(read data)--> read_handler
read_handler --(read data)--> read_handler
Your server loks like this:
<start> --(accept)-----> accept handler
accept_handler --(write data)-> write_handler
write_handler ---------------> <end>
Since your write_handler does not do anything, it makes a transition to the end state, meaning ioservice::run returns. The question now is, what do you want to do, after the data has been written to the socket? Depending on that, you will have to define a corresponding transition, i.e. an async-call that does what you want to do.
Update:
from your comment I see you want to wait for the next data to be ready i.e. for the next tick. The transitions then look like this:
write_handler --(wait for tick/data)--> dataready
dataready --(write data)----------> write_handler
You see, this introduces a new state (handler), I called it dataready, you could as well call it tick_handler or something else. The transition back to the write_handler is easy:
void dataready()
{
// get the new data...
async_write(sock, buffer(data), write_handler);
}
The transition from the write_handler can be a simple async_wait on some timer. If the data come from "outside" and you don't know exactly when they will be ready, wait for some time, check if the data are there, and if they are not, wait some more time:
write_handler --(wait some time)--> checkForData
checkForData:no --(wait some time)--> checkForData
checkForData:yes --(write data)------> write_handler
or, in (pseudo)code:
void write_handler(const error_code &ec, size_t bytes_transferred)
{
//...
async_wait(ticklenght, checkForData);
}
void checkForData(/*insert wait handler signature here*/)
{
if (dataIsReady())
{
async_write(sock, buffer(data), write_handler);
}
else
{
async_wait(shortTime, checkForData):
}
}
Update 2:
According to your comment, you already have an agent that does the time management somehow (calling update every tick). Here's how I would solve that:
Let the agent have a list of observers that get notified when there is new data in an update call.
Let each observer handle one client connection (socket).
Let the server just wait for incomming connections, create observers from them and register them with the Agent.
I am not very firm in the exact syntax of ASIO, so this will be handwavy pseudocode:
Server:
void Server::accept_handler()
{
obs = new Observer(socket);
agent.register(obs);
new socket; //observer takes care of the old one
async_accept(..., accept_handler);
}
Agent:
void Agent::update()
{
if (newDataAvailable())
{
for (auto& obs : observers)
{
obs->notify(data);
}
}
}
Observer:
void Observer::notify(data)
{
async_write(sock, data, write_handler);
}
void Observer::write_handler(error_code ec, ...)
{
if (!ec) //no error: done, just wait for the next notification
return;
//on error: close the connection and unregister
agent.unregister(this);
socket.close(); //client will get error and exit its read_handler
}
I have adapted step 3 of the Boost asio tutorial to run forever, and display "tick" and "tock" once per second instead of the counter:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print(const boost::system::error_code& /*e*/,
boost::asio::deadline_timer* t, int* count)
{
if( !((*count) % 2) )
std::cout << "tick\n";
else
std::cout << "tock\n";
++(*count);
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
int main()
{
boost::asio::io_service io;
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
std::cout << "Final count is " << count << "\n";
return 0;
}
Now I want to asynchronously be able to handle a keypress on stdin. Is there an io_service handler I can use to respond to keypresses, without blocking sleeps or waits?
For example, I'd like to be able to implement a handler function similar to:
void handle_keypress(const boost::error_code&,
char c)
{
std::cout << "Tap '" << c << "'\n";
}
And I would expect my invocation of this handler to be something along the lines of:
char c = 0;
boost::asio::stdin_receiver sr(io);
st.async_wait(boost::bind(handle_keypress, boost::asio::placeholders::error, &c));
io.run();
Is this something I can do with asio, either by using a builtin service handler, or writing my own?
EDIT, ELABORATION:
I have seen this question, but the linked-to code in the accpeted answer simply does this in main:
while (std::cin.getline(
The application I'm writing isn't this simple tick-tock-tap gizmo I've outlined above, but will be a multicast server. Several worker threads will be sending packets to multicast groups, responding to messages from the main thread, and sending messages back to the main thread. The application, in turn, will be "driven" by input from the stdin -- for example, when the user presses the "P" key, multicast broadcast will be paused, and when the hit "Q" the whole thing will shut down. In the main thread, all I'll do in response to these inputs is send messages to the worker threads.
The while loop above won't work in my scenario because while it's waiting for stdin input from the user, the main thread will not be able to process messages coming in from the worker threads. Some of those messages from the worker thread will generate output to stdout.
I don't believe the posix chat client uses a while loop or invokes std::getline, which is the sample code you linked to in my previous answer. Perhaps you are referring to another example? In any case, you don't need to use io_service::dispatch or even a separate thread. The built-in facilities of a stream descriptor work just fine here. See my previous answer to a similar question: Use a posix::stream_descriptor and assign STDIN_FILENO to it. Use async_read and handle the requests in the read handlers.
I've modified your sample code with one way to accomplish this
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print(const boost::system::error_code& /*e*/,
boost::asio::deadline_timer* t, int* count)
{
if( !((*count) % 2) )
std::cout << "tick\n";
else
std::cout << "tock\n";
++(*count);
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
class Input : public boost::enable_shared_from_this<Input>
{
public:
typedef boost::shared_ptr<Input> Ptr;
public:
static void create(
boost::asio::io_service& io_service
)
{
Ptr input(
new Input( io_service )
);
input->read();
}
private:
explicit Input(
boost::asio::io_service& io_service
) :
_input( io_service )
{
_input.assign( STDIN_FILENO );
}
void read()
{
boost::asio::async_read(
_input,
boost::asio::buffer( &_command, sizeof(_command) ),
boost::bind(
&Input::read_handler,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
void read_handler(
const boost::system::error_code& error,
const size_t bytes_transferred
)
{
if ( error ) {
std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl;
return;
}
if ( _command != '\n' ) {
std::cout << "command: " << _command << std::endl;
}
this->read();
}
private:
boost::asio::posix::stream_descriptor _input;
char _command;
};
int main()
{
boost::asio::io_service io;
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
Input::create( io);
io.run();
std::cout << "Final count is " << count << "\n";
return 0;
}
compile, link, and run
samm:stackoverflow samm$ g++ -I /opt/local/include stdin.cc -L /opt/local/lib -lboost_system -Wl,-rpath,/opt/local/lib
samm:stackoverflow samm$ echo "hello world" | ./a.out
command: h
command: e
command: l
command: l
command: o
command:
command: w
command: o
command: r
command: l
command: d
read error: End of file
tick
tock
tick
tock
tick
tock
tick
tock
tick
tock
^C
samm:stackoverflow samm$
There is always the option of handling stdin in a separate thread and posting any keypresses to your main event loop via io_service::dispatch
This function is used to ask the io_service to execute the given handler.
The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked.
Under Linux the key to getting STDIN keypress events lies in the terminal configuration. You can unset the ICANON flag via tcsetattr() and then you will get every single key pressed event. Here is an example program, which demonstrates that. Note that the buffer is larger than one, so we can get more characters at once as well, if they are availabe (for example when you copy&paste).
#include <boost/asio.hpp>
#include <iostream>
int main()
{
boost::asio::io_service ioservice;
boost::asio::posix::stream_descriptor in(ioservice, STDIN_FILENO);
char buf[10];
struct termios term_old;
tcgetattr(STDIN_FILENO, &term_old);
struct termios newt = term_old;
newt.c_lflag &= ~(ICANON); // don't wait until EOL
newt.c_lflag &= ~(ECHO); // don't echo
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
std::function<void(boost::system::error_code, size_t)> read_handler
= [&](boost::system::error_code ec, size_t len) {
if (ec) {
std::cerr << "exit: " << ec.message() << std::endl;
} else {
buf[len] = '\0';
std::cout << "in: " << buf << std::endl;
in.async_read_some(boost::asio::buffer(buf), read_handler);
}
};
in.async_read_some(boost::asio::buffer(buf), read_handler);
ioservice.run();
tcsetattr(STDIN_FILENO, TCSANOW, &term_old);
}