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()
Related
Here is my code.
boost::asio::async_write(*serialPort, boost::asio::buffer(*something),handler);
boost::asio::async_write(*serialPort, boost::asio::buffer(*something2),handler);
The above code will get an error "the requested resource is in use" on the second line (Note that the async stream is a serial port). But when I changed the stream to a tcp socket, everthing works fine. Why?
Now I know I can’t use these composed asynchronous operation these way, but the first line code may be a heartbeat package, the second line may be a package which don’t sent regually. And these send operations buffers can’t gather together in the same time. Is there a way to synchronize these asynchronous operations in a single thread ( or multi-thread)?
The commenters are right. In this case you can easily use a buffer sequence ("scatter/gather IO"):
std::vector<boost::asio::const_buffer> buffers {
boost::asio::buffer(*something),
boost::asio::buffer(*something2)
};
boost::asio::async_write(*serialPort, buffers, handler);
See it Compiling On Coliru
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
void handler(boost::system::error_code ec, size_t) {
std::cout << __PRETTY_FUNCTION__ << ": " << ec.message() << "\n";
}
int main() {
boost::asio::io_service svc;
auto serialPort = std::make_shared<boost::asio::serial_port>(svc);
auto something = std::make_shared<std::string>("hello world\n");
auto something2 = std::make_shared<std::string>("bye world\n");
std::vector<boost::asio::const_buffer> buffers {
boost::asio::buffer(*something),
boost::asio::buffer(*something2)
};
boost::asio::async_write(*serialPort, buffers, handler);
}
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
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.
As I understand it, I should be able to use a boost:asio asynchronous timer to trigger a callback every n milliseconds whilst my program is doing something else without needing threads. Is that assumption correct ?
I put together the following test program which just prints the handler messages and never prints the rand() values. What I want is to see all the floating point numbers scroll down the screen, then every 250ms a handler message should appear in amongst them.
Here is the code :
#include <iostream>
#include <vector>
#include <cstdlib>
#include <boost/asio.hpp>
#include <boost/date_time.hpp>
#include <boost/thread.hpp>
boost::asio::io_service io_service;
boost::posix_time::time_duration interval(boost::posix_time::milliseconds(250));
boost::asio::deadline_timer timer(io_service,interval);
void handler(const boost::system::error_code& error);
void timer_init() {
timer.expires_at(timer.expires_at()+interval);
timer.async_wait(handler);
}
void handler(const boost::system::error_code& error) {
static long count=0;
std::cout << "in handler " << count++ << std::endl;
std::cout.flush();
timer_init();
}
int main(int argc, char **argv) {
timer.async_wait(handler);
io_service.run();
std::vector<double> vec;
for (long i=0; i<1000000000; i++) {
double x=std::rand();
std::cout << x << std::endl;
std::cout.flush();
vec.push_back(x);
}
return 0;
}
This:
io_service.run();
Is a blocking call. It's true that you can have multiple things happening asynchronously in one thread using ASIO, but you cannot have ASIO running in the same thread as code which is not integrated with ASIO. This is a classic event-driven model, where all the work gets done in response to some readiness notification (timers, in your case).
Try moving your vector/rand code to a function and passing that function to io_service::post(), which will then run that code within the context of its run() method. Then when you invoke run(), both things will happen (though not truly concurrently, as that would require threads).
As John Zwinck mentioned, io_service::run() blocks - it's a main asio loop that dispatches completion handlers. However, instead of calling run, you can "manually" process the io_service queue by interleaving io_service::poll_one with your loop:
for (long i=0; i<1000000000; i++) {
double x=std::rand();
std::cout << x << std::endl;
std::cout.flush();
vec.push_back(x);
io_service.poll_one();
}
I created a console application that sends data on a network link. I used the boost library, both the thread and the asio ones; currently i'm running it under Windows. If I run a single application it works perfectly, but if I open two instances on two different consoles, the CPU load goes to 100%, if I close one of the application it goes back to normal. I just used a simple socket with async reads and writes, and threads with condition variables and mutexes. Is there any special thingh to do when dealing with such a situation? I can show you some code, but I think it's nothing special:
socket->connect(tcp::endpoint(address::from_string(getAddress()),getPort()));
for connecting
and
socket->async_read_some(buffer(receiveData),bind(&NetworkLink::handle_response, this,placeholders::error,placeholders::bytes_transferred));
inside the handle_response function for async reading.
For the thread I use
boost::unique_lock<boost::mutex> messages_lock(message_received_mutex);
Before deleting everything and starting a simple test project from scratch I would like to know if there are any special care to be taken in this situation.
Ok it seems I've tracked down the problem. First of all, the 100% CPU usage was due to the fact that each instance was using 50% of the CPU (I'm on a dual core PC). So I run all over the code and found out this. I had this in my code, inside the NetworkLink::handle_response function:
socket->async_read_some(
boost::asio::buffer(receiveData),
boost::bind(&NetworkLink::handle_response, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
io_service.run();
I used the io_service.run(); because before the software was not receiving data. Now I removed the line, I don't get the 50% CPU usage, but the handler response is not called so I cannot receive any data. Any thought about this?
Thanks
PS: I created a small app that shows this problem:
#include <iostream>
#include <boost/asio.hpp>
#include "boost/thread.hpp"
#include "boost/thread/mutex.hpp"
#ifdef _WIN32
#include "Windows.h"
#endif
using namespace boost::asio::ip;
using namespace std;
std::vector<uint8_t> buf;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket mysocket(io_service);
int handle_response(const boost::system::error_code &err,
size_t bytes_transferred)
{
// cout << bytes_transferred << ' ';
if (bytes_transferred > 0)
cout << buf.data() << ' ';
boost::asio::async_read(mysocket, boost::asio::buffer(buf),
boost::asio::transfer_at_least(1), &handle_response);
}
int mythread()
{
boost::asio::async_read(mysocket, boost::asio::buffer(buf),
boost::asio::transfer_at_least(1), &handle_response);
io_service.run();
}
int main()
{
int m_nPort = 12345;
buf.resize(100, '0');
boost::condition_variable message_received_condition;
boost::thread message_receiver_thread(&mythread);
boost::mutex messages_mutex;
tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"),
m_nPort);
boost::unique_lock<boost::mutex> messages_lock(messages_mutex);
message_received_condition.notify_one();
cout << "Waiting for connection..." << endl;
Sleep(10000);
mysocket.connect(endpoint);
cout << "connection accepted" << endl;
try
{
while (true)
{
boost::system::error_code ec;
boost::asio::socket_base::bytes_readable command(true);
mysocket.io_control(command);
std::size_t bytes_readable = command.get();
mysocket.async_read_some(boost::asio::buffer(buf),
&handle_response);
io_service.run();
}
} catch (exception &e)
{
cerr << e.what() << endl; //"The parameter is incorrect" exception
}
}
If you remove the comment from th line
// cout << bytes_transferred << ' ';
in the handle response functionyou get a lower CPU usage, I guess because of the delay for writing to the screen.
You are not checking the error. If there is a failure, you continue to read anyway, which will probably immediately post back a completion with a failure, ad infinitum.