I am trying to listen for input on a named pipe. I'm using Boost.Asio's stream_descriptor and async_read under Linux. The problem is, the call to io_service::run() only blocks like I want it to until the first read. After that, it just keeps calling the handler immediately with the "End of file" error, even though I try to attach more async_reads to it. The code I have is equivalent to the following:
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true)
{
// buffer and handler probably aren't important for the purposes of this question
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
}
Only the first async_read works as I expect it to. Subsequent async_reads just return immediately. The only way I found to make it work like I want is to close and reopen the named pipe, but it seems like a hack:
boost::asio::io_service io_service;
while (true)
{
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
close(fifo_d);
}
Can anyone tell me what am I doing wrong?
UPDATE: Here's a simple "read" version, which allowed for some code simplification, the problem remains the same:
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true) {
try {
boost::asio::read(fifo, boost::asio::buffer(buffer));
}
catch (boost::system::system_error& err) {
// It loops here with "read: End of file" error
std::cout << err.what() << std::endl;
}
}
This is not how works. run() is not intended to be called in a loop. If you insist, you need to call reset() in between (as per the documentation).
Also, if you /want/ blocking behaviour, why are you using the async_* interface?
Demos
Consider using a simple iostream to read the fd:
Live On Coliru
#include <iostream>
#include <fstream>
int main() {
std::ifstream fifo("/tmp/fifo");
std::string word;
size_t lineno = 0;
while (fifo >> word) {
std::cout << "word: " << ++lineno << "\t" << word << "\n";
}
}
Or if you must attach to some fd you get from somewhere else, use file_descriptor from Boost IOstreams:
Live On Coliru
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
#include <fcntl.h>
int main() {
namespace io = boost::iostreams;
using src = io::file_descriptor_source;
io::stream<src> fifo(src(open("./fifo", O_RDONLY), io::file_descriptor_flags::close_handle));
std::string word;
size_t number = 0;
while (fifo >> word) {
std::cout << "word: " << ++number << "\t" << word << "\n";
}
}
Both examples print the expected:
word: 1 hello
word: 2 world
As also sehe reported, that's not the way boost::asio works.
The ioservice::run() method runs in blocking mode while it has some work. When the ioservice goes out of work you have to call the reset() method before putting other work, so that's why in your first code the async_read is done only once.
A common pattern in this case would look something like:
void handler(...) {
if (!error) {
// do your work
boost::asio::async_read(fifo, buffer, handler); // <-- at the end of the handler a subsequent async_read is put to the ioservice, so it never goes out-of-work
}
}
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler); // <-- you call async_read only once here.
io_service.run(); //<-- this blocks till an error occurs
Related
In the following example I try to write some data to a child process, which processes the data and writes it to a file. After closing the stream the parent process waits indefinitely for the child to finish. I am at a loss to know how to indicate that I’m done writing the data and would like the child process to stop reading and finish whatever it is doing. According to the documentation calling terminate would send a SIGKILL which I don’t think is what I want.
What am I missing? I checked this question but I would rather try to make the actual code work with synchronous IO first.
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main(int argc, char **argv)
{
boost::process::opstream in{};
boost::process::child child("/path/to/test.py", bp::std_in < in);
in << "test1\n";
in << "test2\n";
in << "test3\n";
in << std::flush;
std::cerr << "Closing the stream…\n";
in.close();
std::cerr << "Waiting for the child to exit…\n";
child.wait(); // Parent seems to hang here.
return 0;
}
test.py just writes the data to a file like so:
#!/usr/local/homebrew/opt/python#3.8/bin/python3
import sys
with open("/tmp/test.txt", "w") as f:
for line in sys.stdin:
f.write(line)
After inspecting the source code, I found out that closing the stream did not close the associated pipe at least in this case. Doing that manually did solve the issue:
...
in.close();
in.pipe().close();
child.wait(); // Does not hang.
The documentation warns that using synchronous IO to child processes is prone to deadlock.
Here's a minimal reword to async IO:
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main() {
boost::asio::io_context ioc;
bp::async_pipe in{ioc};
bp::child child("./test.py", bp::std_in < in, bp::std_out.close());
for (auto msg : { "test1\n", "test2\n", "test3\n" }) {
write(in, bp::buffer(msg, strlen(msg)));
}
std::cerr << "Closing the pipe…\n";
in.close();
std::cerr << "Waiting for the child to exit…\n";
ioc.run(); // already awaits completion
child.wait(); // Parent seems to hang here.
}
You can make it more realistic by doing some delays:
#include <boost/process.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace bp = boost::process;
int main() {
boost::asio::io_context ioc;
bp::async_pipe in{ioc};
bp::child child("./test.py", bp::std_in < in, bp::std_out.close());
std::thread th([&] {
for (auto msg : { "test1\n", "test2\n", "test3\n" }) {
write(in, bp::buffer(msg, strlen(msg)));
std::this_thread::sleep_for(1s);
}
std::cerr << "Closing the pipe…\n";
in.close();
});
std::cerr << "Waiting for the child to exit…\n";
ioc.run(); // already awaits completion
th.join();
child.wait(); // Parent seems to hang here.
}
For fullblown async IO see other examples:
simultaneous read and write to child's stdio using boost.process
How to retrieve program output as soon as it printed?
Running a process using boost process in async mode with timeout
I'm trying to create an s-function (using C++ Boost library) for UDP communication.
Implementing the sender was fairly straightforward, 15 min job. I'm struggling to get the receiver to work.
I created the following in Visual Studio:
#define _WIN32_WINNT 0x0501
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <stdio.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;
using boost::asio::ip::udp;
using namespace std;
std::vector<char> receive_buffer;
void process_received_frame(const boost::system::error_code& error, size_t received_frame_size) {
if (error) {
cout << "Receive failed: " << error.message() << "\n";
return;
}
size_t ByteCount = 0;
std::cout << endl << "Received byte stream (Handler) [" << received_frame_size << "]: ";
for (std::vector<char>::const_iterator iter = receive_buffer.cbegin(); iter != receive_buffer.cend(); iter++)
{
ByteCount++;
printf("%02X ", (UINT8)*iter);
if (ByteCount == received_frame_size)
{
break;
}
}
std::cout << endl;
}
int main(int argc, char *argv[])
{
boost::asio::io_service io_service;
udp::socket socket(io_service);
udp::endpoint remote_endpoint = udp::endpoint(boost::asio::ip::address_v4::from_string("127.0.0.1"), 19001);
socket.open(udp::v4());
socket.bind(udp::endpoint(remote_endpoint));
receive_buffer.resize(255);
try
{
socket.async_receive_from(boost::asio::buffer(receive_buffer),
remote_endpoint,
boost::bind(&process_received_frame, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
catch (const std::exception& exp)
{
printf("%s\n", exp.what());
}
//io_service.poll();
io_service.run();
cout << "End";
std::cin.ignore();
}
I tried sending UDP to localhost:19001 from Simulink and was able to receive the UDP packets in Visual Studio. The handler (process_received_frame) gets called and everything seems to work, as expected.
But, given that, io_service::run() works in blocking mode, it pauses execution if there is nothing received on port 19001. So I tried using io_service::poll() (commented in the code above) instead. However, when I use poll(), it does not execute the handler. If I try to display the contents of 'receive_buffer' from main(), I get all 0s. Interestingly, when I single-step through the code for accessing the elements of 'receive_buffer' I do get the right values.
Not sure what is it that I'm doing wrong. Quite likely to be a school-boy-error.
When I convert this to an s-function for MATLAB-Simulink, it does the same thing - all zeros.
Any help would be much appreciated.
Cheers,
In your handler function, you need to call socket.async_receive_from at the end after processing the answer. io_service.run() returns when no more handler are in its processing queue.
See the example from boost doc here: udp sync server example
EDIT
Rereading your question/comment, I'm not sure what your expected output or behavior is.
If you're only expecting a single UDP frame, then maybe call io_service.run_one().
If you don't want run() to block your main thread, you need to launch another thread to call run(). Something like:
boost::asio::io_service io_service;
// Process handlers in a background thread.
boost::thread t(boost::bind(&io_service::run, &io_service));
...
io_service::run() is always a blocking call. Completion handlers can only be called from threads currently calling run(). The only time run() is going to return is when there is no more handlers in the queue (you stopped calling async_receive) or if you explicitly cancel the run() command by calling stop() or reset()
I started to learn the boost::asio and tried to make simple client-server application. At now I have troubles with server. Here it code:
int main(int argc, char* argv[])
{
using namespace boost::asio;
io_service service;
ip::tcp::endpoint endp(ip::tcp::v4(), 2001);
ip::tcp::acceptor acc(service, endp);
for (;;)
{
socker_ptr sock(new ip::tcp::socket(service));
acc.accept(*sock);
for (;;)
{
byte data[512];
size_t len = sock->read_some(buffer(data)); // <--- here exception at second iteration
if (len > 0)
write(*sock, buffer("ok", 2));
}
}
}
It correctly accepted the client socket, correctly read, then it write data and strarted new iteration. On second iteration throwed exception. It looks like:
And I don`t get why it happens?
I just need that server must read/write continuosly while the client present. And when the client gone the server must accept next client.
So the main question: why excpection happens and what how to aviod it?
...
UPDATE1: I found that at first iteration the error code of both read/write operation is successful. But (!) on second iteration at place where exception reised the error code is "End of file". But why?
You get the end of file condition because the remote end of the connection closed or dropped the connection.
You should be handling the system errors, or using the overloads that take a reference to boost::system::error_code. How else would you ever terminate the infinite loop?
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
int main()
{
using namespace boost::asio;
io_service service;
ip::tcp::endpoint endp(ip::tcp::v4(), 2001);
ip::tcp::acceptor acc(service, endp);
for (;;)
{
ip::tcp::socket sock(service);
acc.accept(sock);
boost::system::error_code ec;
while (!ec)
{
uint8_t data[512];
size_t len = sock.read_some(buffer(data), ec);
if (len > 0)
{
std::cout << "received " << len << " bytes\n";
write(sock, buffer("ok", 2));
}
}
std::cout << "Closed: " << ec.message() << "\n";
}
}
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'm looking for a way to skip user input after X seconds.
Something like:
std::cout << "Enter a number \n";
timer(5000)
{
std::cin >> number;
}
//the rest of the program.
Anything like this exist?
I'm pretty sure there's nothing that's even close to portable. For that matter, there's probably nothing that works directly with iostreams.
Given that you want it for Windows, you could use the console API directly. In particular, you can use WaitForSingleObject on the standard input handle, and give a timeout:
HANDLE standard_input = GetStdHandle(STD_INPUT_HANDLE);
if (WaitForSingleObject(standard_input, 5000) == WAIT_OBJECT_0)
// read what the user entered
else
// timed out (or otherwise failed).
Not really: even if this input is executed on a separate thread, operator>>() is not a cancellation point.
But, just for fun of it, here's a contrived approach using boost.asio:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
boost::asio::streambuf readbuffer(100);
int n = 0;
void readhandler(const boost::system::error_code& error,
size_t bytes_transferred)
{
if(!error)
{
const char* buf = boost::asio::buffer_cast<const char*>(readbuffer.data());
std::string result(buf, bytes_transferred);
std::istringstream is(result);
is >> n;
}
}
int main()
{
boost::asio::io_service ioserv;
boost::asio::posix::stream_descriptor streamdesc(ioserv);
streamdesc.assign(0); // filedesc of stdin
streamdesc.async_read_some(
readbuffer.prepare(readbuffer.max_size()),
boost::bind(readhandler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
std::cout << "You have 5 seconds to enter a number: " << std::flush;
boost::thread server(boost::bind(&boost::asio::io_service::run, &ioserv));
boost::this_thread::sleep(boost::posix_time::seconds(5)); // or timed_wait
ioserv.stop();
server.join();
std::cout << "The number entered was " << n << '\n';
}