How to test an instance counter by asynchronous run of a boost childprocess? - boost

I have tried to use boost::childprocess with an async_pipe as shown in the code example below, while expecting since there is a wait method, that the call to run would not wait for the called executable to finish before continuing to the line where I call wait(). My aim is namely to start the same executable multiple times in order to test in GTest an instance counting method (implemented based on boost managed shared memory segment).
But here fore I need the call to io_service::run(), to not wait for the called executable to finish as it does right now. Can someone tell me where I am using it wrong please? Or if this is the wrong way to unit test my function? I have been trying to find the solution for quite some time!
Here is a sample of how I call one instance of the executable:
int CallChildProcess_Style9() {
std::string strCmdLine = "E:\\file.exe --Debug MainStartUps_Off --Lock 3";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
m_pChildProcess.reset(new bp::child(strCmdLine.data(), bp::std_out > m_oAsyncPipe_Out, build_ec));
m_oAsyncBuffer_Out.resize(1024*8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) { nReadSize = size; });
size_t iii = m_oIOS.run();
m_pChildProcess->wait();
m_oAsyncBuffer_Out.resize(nReadSize);
std::string strBuf(m_oAsyncBuffer_Out.begin(), m_oAsyncBuffer_Out.begin() + nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}

Using io_service
To be using async_pipe, you need to supply the io_service instance to the parameter keywords of bp::child:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
namespace bp = boost::process;
int CallChildProcess_Style9() {
std::string strCmdLine = "/bin/cat";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
std::vector<std::string> const args = { "/home/sehe/Projects/stackoverflow/test.cpp" };
m_pChildProcess.reset(new bp::child(strCmdLine, args, bp::std_out > m_oAsyncPipe_Out, build_ec, m_oIOS));
std::cout << "Launched: " << build_ec.message() << std::endl;
m_oAsyncBuffer_Out.resize(1024 * 8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
});
std::cout << "read started" << std::endl;
size_t iii = m_oIOS.run();
std::cout << "io_service stopped" << std::endl;
std::cout << "initiate child::wait" << std::endl;
m_pChildProcess->wait();
std::cout << "wait completed" << std::endl;
std::string const strBuf(m_oAsyncBuffer_Out.data(), nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}
int main() {
CallChildProcess_Style9();
}
Prints
http://coliru.stacked-crooked.com/a/8a9bc6bed3dd5e0a
Launched: Success
read started
read completion handler: size = 1589 (End of file)
io_service stopped
initiate child::wait
wait completed
Hanging Up The Child
Even with that fixed, async_pipe::async_read only reads until the buffer is full or EOF is reached. If the child process outputs more than the buffer size (8k in your sample) then it will get stuck and never finish.
E.g.: replacing the command like this:
std::string strCmdLine = "/usr/bin/yes";
Results in
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
At which it will hang till infinity. This is not because yes has infinite output. Any command having large output will hang (e.g. /bin/cat /etc/dictionaries-common/words hangs in the same way). You can prove this by looking at the strace output:
$ sudo strace -p $(pgrep yes)
strace: Process 21056 attached
write(1, "/home/sehe/Projects/stackoverflo"..., 8170
The easiest way to "fix" this would be to close the output sink after you filled up your output buffer:
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
m_oAsyncPipe_Out.close();
});
This requires you to anticipate that the child exited before you call wait() so wait() might fail:
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
wait completed (Success)
Taking A Step Back: What Do You Need?
It looks, though, that you might be complicating. If you're happy limiting the output to 8k, and all you need is to have multiple copies, why bother with async io?
Any child is already asynchronous, and you can just pass the buffer:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
using Args = std::vector<std::string>;
using Buffer8k = std::array<char, 8192>;
int main() {
auto first_out = std::make_unique<Buffer8k>(),
second_out = std::make_unique<Buffer8k>();
*first_out = {};
*second_out = {};
boost::asio::io_service svc;
bp::child first("/bin/echo", Args{"-n", "first"}, bp::std_out > boost::asio::buffer(*first_out), svc);
bp::child second("/bin/echo", Args{"-n", "second"}, bp::std_out >boost::asio::buffer(*second_out), svc);
std::cout << "Launched" << std::endl;
svc.run();
first.wait();
second.wait();
std::string const strFirst(first_out->data()); // uses NUL-termination (assumes text output)
std::string const strSecond(second_out->data()); // uses NUL-termination (assumes text output)
std::cout << strFirst << "\n";
std::cout << strSecond << "\n";
return first.exit_code();
}
Prints
Launched
first
second
More Examples
Because I can't really be sure about what you need, look at other examples that I wrote to actually show live async IO, where you might need to respond to particular output of one process.
Boost::process output blank lines
Read child process stdout in a separate thread with BOOST process
How to retrieve program output as soon as it printed?

Related

Can't get segmentation fault exit code from boost child process

I am trying to get the exit code of a child process (using boost::process and boost::asio) when that child process is killed due to a segmentation violation or divide be zero or any other kill signal. The exit code and error code always return with 0 and success.
I am running this on CentOS 7 using g++ 4.8.5 and boost 1.66
If I run the same code with a child process that simply returns a non-zero exit code it successfully returns that exit code.
#include <iostream>
#include <boost/process.hpp>
#include <boost/asio/io_service.hpp>
namespace bp = boost::process;
using namespace std;
int main (int argc, char** argv)
{
string exe = "./crashes";
vector<string> data;
boost::asio::io_service ios;
int exit_code;
error_code ec;
future<string> ostr;
bp::child c(exe,
(bp::std_out & bp::std_err) > ostr,
ios,
bp::on_exit=[&exit_code, &ec](int exit, const error_code& ecin)
{exit_code = exit; ec = ecin;});
ios.run();
cout << "Exit Code = " << exit_code << endl;
cout << "Error Code = " << ec.message() << endl;
cout << "child stdin & stderr:\n";
cout << ostr.get() << endl;
return exit_code;
}
and the crashes code
int main (int argc, char** argv)
{
int* y = 0;
int c = *y;
}
The results show a 0 exit code and Success error_code
Exit Code = 0
Error Code = Success
child stdin & stderr:
running the crashes executable alone returns an exit code of 139
bash-4.2$ ./crashes
Segmentation fault (core dumped)
bash-4.2$ echo $?
139
The details of process termination and exit codes are platform dependent.
Boost process papers over the differences in the default interface: your on_exit handler is called with the result of boost::process::detail::posix::eval_exit_status() of the exit status, which means:
inline int eval_exit_status(int code)
{
if (WIFEXITED(code))
{
return WEXITSTATUS(code);
}
else if (WIFSIGNALED(code))
{
return WTERMSIG(code);
}
else
{
return code;
}
}
So, you get "exit-code 11" meaning segfault... If you want to actually know, you can look at native_exit_code()
bp::on_exit = [&result, &c](int /*ignored*/, const std::error_code &ec) {
auto exit_status = c.native_exit_code();
result.exit_code = boost::make_optional(WIFEXITED(exit_status), WEXITSTATUS(exit_status));
result.signal = boost::make_optional(WIFSIGNALED(exit_status), WTERMSIG(exit_status));
result.ec = ec;
}
Now this assumes some changes to the result variables. Full listing:
Listing
#include <boost/asio/io_service.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main(int argc, char**) {
std::string exe = argc>1? "./ltua" : "./crashes";
boost::asio::io_service ios;
struct {
boost::optional<int> exit_code;
boost::optional<int> signal;
std::error_code ec{};
} result;
std::future<std::string> ostr;
bp::group g;
bp::child c(exe, g, (bp::std_out & bp::std_err) > ostr, ios,
bp::on_exit = [&result, &c](int /*ignored*/, const std::error_code &ec) {
auto exit_status = c.native_exit_code();
result.exit_code = boost::make_optional(WIFEXITED(exit_status), WEXITSTATUS(exit_status));
result.signal = boost::make_optional(WIFSIGNALED(exit_status), WTERMSIG(exit_status));
result.ec = ec;
});
//g.wait();
ios.run();
if (result.exit_code) {
std::cout << "Exited with " << *result.exit_code << std::endl;
}
if (result.signal) {
std::cout << "Signaled with sginal #" << *result.signal << ", aka " << ::strsignal(*result.signal) << std::endl;
}
std::cout << "Error Code = " << result.ec.message() << std::endl;
std::cout << "child stdin & stderr:\n";
std::cout << ostr.get() << std::endl;
return result.exit_code? *result.exit_code : 255;
}
Output
When run with ltua.cpp:
#include <iostream>
int main() {
std::cout << "so long" << std::end;
std::cerr << "and thanks" << std::end;
std::cout << "for all" << std::end;
std::cerr << "the fish" << std::end;
return 42;
}
Prints
Exited with 42
Error Code = Success
child stdin & stderr:
so long
and thanks
for all
the fish
And with crashes.cpp:
int main() {
int *y = 0;
int c = *y;
}
Prints
Signaled with sginal #11, aka Segmentation fault
Error Code = Success
child stdin & stderr:

How to retrieve program output as soon as it printed?

I have a boost::process::child. There are many examples on how to get all its stdout or stderr in a single vector, but in this method you capture all data at once. But how to retrieve lines/characters as soon as they are printed in child process?
The docs are here:
Synchronous IO
Asynchronous IO
Using ipstream
The simplest way:
Live On Coliru
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main() {
std::vector<std::string> args {
"-c",
R"--(for a in one two three four; do sleep "$(($RANDOM%2)).$(($RANDOM%10))"; echo "line $a"; done)--" };
bp::ipstream output;
bp::child p("/bin/bash", args, bp::std_out > output);
std::string line;
while (std::getline(output, line)) {
std::cout << "Received: '" << line << "'" << std::endl;
}
}
Prints (e.g.):
At 0.409434s Received: 'line one'
At 0.813645s Received: 'line two'
At 1.2179s Received: 'line three'
At 2.92228s Received: 'line four'
Using async_pipe
This method is much more versatile and lets you do the "hard" cases where deadlock could occur, like when you want to do other things at the same time instead of blocking for input.
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/asio.hpp>
#include <iostream>
namespace bp = boost::process;
using boost::asio::mutable_buffer;
void read_loop(bp::async_pipe& p, mutable_buffer buf) {
p.async_read_some(buf, [&p,buf](std::error_code ec, size_t n) {
std::cout << "Received " << n << " bytes (" << ec.message() << "): '";
std::cout.write(boost::asio::buffer_cast<char const*>(buf), n) << "'" << std::endl;
if (!ec) read_loop(p, buf);
});
}
int main() {
boost::asio::io_service svc;
std::vector<std::string> args {
"-c",
R"--(for a in one two three four; do sleep "$(($RANDOM%2)).$(($RANDOM%10))"; echo "line $a"; done)--" };
bp::async_pipe output(svc);
bp::child p("/bin/bash", args, bp::std_out > output, svc);
char buf[1024];
read_loop(output, bp::buffer(buf));
svc.run();
}

Serializing multiple std::shared_ptr via boost::serialization and sending over boost::asio

I'd like to transmit a shared_ptr object via boost asio from a client to a server. Here is my code:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("localhost", "3000");
boost::archive::text_oarchive archive(stream);
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
archive << dl;
}
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
std::shared_ptr<Message> m;
try {
archive >> m;
cout << m->text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
int main(int argc, char **argv) {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
return 0;
}
Here is the program output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 3
Hello 3
Hello 3
Hello 3
Client shutdown
Hello 3
Hello 3
input stream error
eof
Server: shutdown client handling...
I am expecting the following output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Client shutdown
Hello 8
Hello 9
input stream error
eof
Server: shutdown client handling...
When changing the shared_ptr to a simple object (std::shared_ptr<Message> m; to Message m) everything works as expected. I want to stick to the shared_ptr. What do I need to change?
Serialization alone seems to work:
stringstream stream;
{
boost::archive::text_oarchive archive(stream);
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello World!";
dl->text = ss.str();
archive << dl;
}
{
boost::archive::text_iarchive archive(stream);
std::shared_ptr<Message> m;
archive >> m;
cout << m->text << endl;
}
Output: Hello World!
The issues you're encountering are due to object tracking done by Boost.Serialization.
Depending on how the class is used and other factors, serialized
objects may be tracked by memory address. This prevents the same
object from being written to or read from an archive multiple times.
These stored addresses can also be used to delete objects created
during a loading process that has been interrupted by throwing of an
exception.
The documentation actually foreshadows this specific issue happening:
This could cause problems in progams[sic] where the copies of different
objects are saved from the same address.
Furthermore, the Class Serialization Traits documentation on object tracking tells us that in this particular situation, object tracking is enabled:
Default tracking traits are:
For primitive, track_never.
For pointers, track_never. That is, addresses of addresses are not tracked by default.
All current serialization wrappers such as boost::serialization::nvp, track_never.
For all other types, track_selectively. That is addresses of serialized objects are tracked if and only if one or more of the
following is true:
an object of this type is anywhere in the program serialized through a pointer.
the class is explicitly "exported" - see below.
the class is explicitly "registered" in the archive
Going back to your situation -- in the client, due to how your loop body is written, the 5th (and following) Message instance were allocated at the same address as the 4th Message instance. You can verify this by inspecting the values of dl.get() in each iteration. (In my tests on coliru, all of the instances were allocated at the same address, so YMMV).
Due to how object tracking works, all those shared_ptr instances were considered to point to the same Message instance (even though you changed the value meanwhile -- the library does not expect this happening), so the additional occurrences were just serialized as additional references. Upon deserialization... to be honest this smells of memory leaks and/or dangling reference issues (opinion, haven't investigated this in detail).
Summed up, the main issue with the code as shown is that it breaks a prerequisite of the serialization library, which is that you're serializing some constant state, and on deserialization you recreate that same state.
One way to address this would be to have an initialized std::vector of shared_ptr<Message> containing all the messages to transmit in this particular transaction. Similarly, you'd deserialize the whole vector on the other side. If you expect to have some persistent connection, then add framing to the protocol, with each frame containing an archive that contains one sequence of messages.
Minimal code modifications to make this work -- add include
#include <boost/serialization/vector.hpp>
Change runClient() as such:
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
std::vector<std::shared_ptr<Message>> messages;
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
messages.emplace_back(dl);
}
boost::archive::text_oarchive archive(stream);
archive << messages;
stream.close();
cout << "Client shutdown" << endl;
}
And change handleIncommingClientConnection(...) as such:
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
std::vector<std::shared_ptr<Message>> messages;
archive >> messages;
for (auto const& m : messages) {
cout << m->text << endl;
}
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
NB: This doesn't add any support for multiple frames -- the client is expected to close the connection after it sent one vector of messages, otherwise the behaviour is undefined.
Sample on Coliru
Further resources:
boost serialization multiple objects

How to determine if there are bytes available to be read from boost:asio:serial_port

I am trying to use boost to communicate serially between my desktop and an arduino. In arduino space, I can ask the serial port whether or not there are bytes available before trying to perform a read.
I am having trouble finding the equivalent for boost::asio::serial_port.
While Boost.Asio does not provide direct support for this, one can still accomplish this by using serial port's native_handle() with system specific calls. Consult the system's documentation to determine how to query for the available bytes ready to be read, but it is often ioctl(..., FIONREAD, ...) on Linux, and ClearCommError() on Windows.
Here is a complete minimal example that uses system specific calls to get the number of bytes available. The example program will continue to query the serial port until there are greater than 20 bytes available, at which point it will read all but 5 bytes:
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
/// #brief Returns the number of bytes available for reading from a serial
/// port without blocking.
std::size_t get_bytes_available(
boost::asio::serial_port& serial_port,
boost::system::error_code& error)
{
error = boost::system::error_code();
int value = 0;
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
COMSTAT status;
if (0 != ::ClearCommError(serial_port.lowest_layer().native_handle(),
NULL, &status))
{
value = status.cbInQue;
}
// On error, set the error code.
else
{
error = boost::system::error_code(::GetLastError(),
boost::asio::error::get_system_category());
}
#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
if (0 == ::ioctl(serial_port.lowest_layer().native_handle(),
FIONREAD, &value))
{
error = boost::system::error_code(errno,
boost::asio::error::get_system_category());
}
#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
return error ? static_cast<std::size_t>(0)
: static_cast<size_t>(value);
}
/// #brief Returns the number of bytes available for reading from a serial
/// port without blocking. Throws on error.
std::size_t get_bytes_available(boost::asio::serial_port& serial_port)
{
boost::system::error_code error;
std::size_t bytes_available = get_bytes_available(serial_port, error);
if (error)
{
boost::throw_exception((boost::system::system_error(error)));
}
return bytes_available;
}
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <device_name>" << std::endl;
return 1;
}
// Create all I/O objects.
boost::asio::io_service io_service;
boost::asio::serial_port serial_port(io_service, argv[1]);
// Continue quering the serial port until at least 20 bytes are available
// to be read.
std::size_t bytes_available = 0;
while (bytes_available < 20)
{
bytes_available = get_bytes_available(serial_port);
std::cout << "available: " << bytes_available << std::endl;
boost::this_thread::sleep_for(::boost::chrono::seconds(3));
}
// Read all but 5 available bytes.
std::vector<char> buffer(bytes_available - 5);
std::size_t bytes_transferred =
read(serial_port, boost::asio::buffer(buffer));
bytes_available = get_bytes_available(serial_port);
// Print results.
std::cout << "Read " << bytes_transferred << " bytes\n";
std::cout.write(&buffer[0], bytes_transferred);
std::cout << "\navailable: " << bytes_available << std::endl;
}
Create virtual serial ports with socat:
$ socat -d -d PTY: PTY
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/2
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/3
2015/02/01 21:12:31 socat[3056] N starting data transfer loop
with FDs [3,3] and [5,5]
After starting the program in one terminal, I write to /dev/pts/3 in another terminal:
$ echo -n "This is" > /dev/pts/3
$ echo -n " an example" > /dev/pts/3
$ echo -n " with asio." > /dev/pts/3
And the resulting output from the program:
$ ./a.out /dev/pts/2
available: 0
available: 7
available: 18
available: 29
Read 24 bytes
This is an example with
available: 5
I don't know of such a thing in asio, but as comments above have already stated, you don't really need it. I have an example of how to use boost asio serial at:
https://github.com/cdesjardins/ComBomb/blob/master/TargetConnection/TgtSerialConnection.cpp
It uses async_read_some to fill a buffer with serial data, the buffer data is then queued up for other parts of the program to process.

Windows boost asio: 10061 in async_receive_from on on async_send_to

I have a fairly large application that works as desired on Linux. I've recently compiled it on Windows 7 using VC2012 and boost asio 1.52 and run into a strange issue:
An async_receive_from followed by an async_send_to on the same UDP socket results in the read completion handler being called with boost::system::error_code 10061:
No connection could be made because the target machine actively refused it
if the send destination is another port on the local host. If the packet is sent to another machine, the read completion handler is not called. After the read completion handler, the write completion handler is called with no error.
The following code replicates the issue:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost::asio;
void read_completion_handler(const boost::system::error_code& ec, std::size_t bytes_received)
{
if (!ec)
cout << "Received " << bytes_received << " successfully" << endl;
else
cout << "Error: " << ec.message() << endl;
}
void write_completion_handler(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
if (!ec)
cout << "Wrote " << bytes_transferred << " successfully" << endl;
else
cout << "Error: " << ec.message() << endl;
}
int main(int argc, char** argv)
{
enum
{
max_length = 1500,
out_length = 100
};
// buffer for incoming data
char data[max_length];
// outgoing data
char out_data[out_length];
// sender endpoint
ip::udp::endpoint sender_endpoint;
// for sending packets: if this localhost, the error occurs
ip::udp::endpoint destination(ip::address::from_string("127.0.0.1"), 5004);
io_service ioService;
ip::udp::socket socket(ioService, ip::udp::endpoint(ip::udp::v4(), 49170));
socket.async_receive_from(
buffer(data, max_length), sender_endpoint,
boost::bind(&read_completion_handler,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
socket.async_send_to( boost::asio::buffer(out_data, out_length),
destination,
boost::bind(&write_completion_handler,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
ioService.run();
cout << "Done" << endl;
return 0;
}
On linux this is never an issue. Does anyone have an explanation? As far as I know, simultaneous reads and writes on the same socket should be ok or is this not the case on Windows? Why the change in behaviour if localhost is the destination?
Yes, it's about 6 months after you asked this question. I'm not even sure how I ended up here. I ran into this problem myself -- but the good news is that it's not a problem.
Some machines return a Destination Unreachable message through ICMP when they aren't listening on the port you sent your message to. Asio translates this to boost::system::errc::connection_refused and/or boost::system::errc::connection_reset. This is a meaningless error since UDP is connectionless. You can safely ignore these two error codes in your async_receive_from handler (ie, if you get one of these errors returned, just call async_receive_from again).
For anyone stumbling on this, read the comment I made above to the first response.
However, if you are by any change encountering the same issue in C#, use this code to get rid of the behavior:
byte[] byteTrue = new byte[4];
byteTrue[byteTrue.Length - 1] = 1;
m_udpClient.Client.IOControl(-1744830452, byteTrue, null);
To disable ICMP PORT_UNREACHABLE on UDP receive, set SIO_UDP_CONNRESET to 0 (not 1, like the other answer suggests):
#ifdef _WIN32
struct winsock_udp_connreset {
unsigned long value = 0;
int name() { return -1744830452; /* SIO_UDP_CONNRESET */ }
unsigned long* data() { return &value; }
};
winsock_udp_connreset connreset{0};
socket.io_control(connreset);
#endif

Resources