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

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

Related

What does unique_ptr<T>::operator= do in terms of deallocation

I'm having troubles understanding fully the assignment operator for unique_ptr. I understand that we can only move them, due to the fact that copy constructor and assignment operators are deleted, but what if
a unique_ptr which contains already an allocation is overwritten by a move operation? Is the content previously stored in the smart pointer free'd?
#include <iostream>
#include <memory>
class A{
public:
A() = default;
virtual void act() const {
std::cout << "act from A" << std::endl;
}
virtual ~A() {
std::cout << "destroyed A" << std::endl;
}
};
class B : public A {
public:
B() : A{} {}
void act() const override {
std::cout << "act from B" << std::endl;
}
~B() override {
std::cout << "destroyed from B " << std::endl;
}
};
int main() {
auto pP{std::make_unique<A>()};
pP->act();
==================== ! =======================
pP = std::make_unique<B>(); // || std::move(std::make_unique<B>())
==================== ! =======================
pP->act();
return 0;
}
When I do
pP = std::make_unique<B>();
does it mean that what was allocated in the first lines for pP (new A()) is destructed automatically?
Or should I opt for:
pP.reset();
pP = std::make_unique<B>();
Yes, see section 20.9.1, paragraph 4 of the C++11 draft standard
Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of
such a transfer, the following postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
if the pre-transfer u.d maintained state, such state has been transferred to u2.d.
As in the case of a reset, u2 must properly dispose of its pre-transfer owned object via the pre-transfer
associated deleter before the ownership transfer is considered complete
In other words, it's cleaning up after itself upon assignment like you'd expect.
Yes, replacing the content of a smart pointer will release the previously-held resource. You do not need to call reset() explicitly (nor would anyone expect you to).
Just for the sake of this particular example. It seems polymorphism in your example didn't allow you to draw clear conclusions from output:
act from A
destroyed A
act from B
destroyed from B
destroyed A
So let's simplify your example and make it straight to the point:
#include <iostream>
#include <memory>
struct A {
explicit A(int id): id_(id)
{}
~A()
{
std::cout << "destroyed " << id_ << std::endl;
}
int id_;
};
int main() {
std::unique_ptr<A> pP{std::make_unique<A>(1)};
pP = std::make_unique<A>(2);
}
which outputs:
destroyed 1
destroyed 2
Online
I hope this leaves no room for misinterpretation.

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

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?

C++ std::unordered_map key custom hashing

I've got the following test.cpp file
#include <string>
#include <functional>
#include <unordered_map>
#include <iostream>
class Mystuff {
public:
std::string key1;
int key2;
public:
Mystuff(std::string _key1, int _key2)
: key1(_key1)
, key2(_key2)
{}
};
namespace std {
template<>
struct hash<Mystuff *> {
size_t operator()(Mystuff * const& any) const {
size_t hashres = std::hash<std::string>()(any->key1);
hashres ^= std::hash<int>()(any->key2);
std::cout << "Hash for find/insert is [" << hashres << "]" << std::endl;
return (hashres);
}
};
}; /* eof namespace std */
typedef std::unordered_map<Mystuff *, Mystuff *>mystuff_map_t;
mystuff_map_t map;
int insert_if_not_there(Mystuff * stuff) {
std::cout << "Trying insert for " << stuff->key1 << std::endl;
if (map.find(stuff) != map.end()) {
std::cout << "It's there already..." << std::endl;
return (-1);
} else {
map[stuff] = stuff;
std::cout << "Worked..." << std::endl;
}
return (0);
}
int main(){
Mystuff first("first", 1);
Mystuff second("second", 2);
Mystuff third("third", 3);
Mystuff third_duplicate("third", 3);
insert_if_not_there(&first);
insert_if_not_there(&second);
insert_if_not_there(&third);
insert_if_not_there(&third_duplicate);
}
You can compile with g++ -o test test.cpp -std=gnu++11.
I don't get what I'm doing wrong with it: the hash keying algorithm is definitely working, but for some reason (which is obviously in the - bad - way I'm doing something), third_duplicate is inserted as well in the map, while I'd wish it wasn't.
What am I doing wrong?
IIRC unordered containers need operator== as well as std::hash. Without it, I'd expect a compilation error. Except that your key is actually MyStuff* - the pointer, not the value.
That means you get the duplicate key stored as a separate item because it's actually not, to unordered_map, a real duplicate - it has a different address, and address equality is how unordered_map is judging equality.
Simple solution - use std::unordered_map<Mystuff,Mystuff> instead. You will need to overload operator== (or there's IIRC some alternative template, similar to std::hash, that you can specialize). You'll also need to change your std::hash to also accept the value rather than the pointer.
Don't over-use pointers in C++, especially not raw pointers. For pass-by-reference, prefer references to pointers (that's a C++-specific meaning of "reference" vs. "pointer"). For containers, the normal default is to use the type directly for content, though there are cases where you might want a pointer (or a smart pointer) instead.
I haven't thoroughly checked your code - there may be more issues than I caught.

ASIO handler arguments and boost::bind, compile time error

I am struggling with compile time errors, and try as I might, I dont see in what way am I doing it wrong or different from handler function signature as set out in documentation/examples. (I am using Boost 1.41 on Linux)
Please help me understand the error! (included below as snippet)
My application has objects whose methods are handlers for async_* functions. Below is the code snippet. The error is reported in the line labelled as "line 58", where I use boost::bind
class RPC {
public:
char recv_buffer[56];
void data_recv (void) {
socket.async_read_some (
boost::asio::buffer(recv_buffer),
boost::bind ( &RPC::on_data_recv, this, _1, _2 )
); // **<<==== this is line 58, that shows up in error listing**
global_stream_lock.lock();
std::cout << "[" << boost::this_thread::get_id()
<< "] data recvd" << std::endl;
global_stream_lock.unlock();
} // RPC::data_recv
void on_data_recv (boost::system::error_code& ec, std::size_t bytesRx) {
global_stream_lock.lock();
std::cout << "[" << boost::this_thread::get_id()
<< "] bytes rcvd: " << std::endl;
global_stream_lock.unlock();
data_recv(); // call function that waits for more data
} // RPC::on_data_recv
}; // RPC class def
There is a huge error output, but the relevant lines seem to be:
../src/besw.cpp:58: instantiated from here
/usr/include/boost/bind/bind.hpp:385: error: no match for call to ‘(boost::_m fi::mf2<void, RPC, boost::system::error_code&, long unsigned int>) (RPC*&, boost::asio::error::basic_errors&, int&)’
/usr/include/boost/bind/mem_fn_template.hpp:272: note: candidates are: R boost::_mfi::mf2<R, T, A1, A2>::operator()(T*, A1, A2) const [with R = void, T = RPC, A1 = boost::system::error_code&, A2 = long unsigned int]
/usr/include/boost/bind/mem_fn_template.hpp:291: note: R boost::_mfi::mf2<R, T, A1, A2>::operator()(T&, A1, A2) const [with R = void, T = RPC, A1 = boost::system::error_code&, A2 = long unsigned int]
make: *** [src/besw.o] Error 1
When I remove the place holders (_1 and _2) and have a handler without arguments, then it compiles and executes without errors. Here's that modified code snippet.
void data_recv (void) {
socket.async_read_some (
boost::asio::buffer(recv_buffer),
boost::bind ( &RPC::on_data_recv, this )
);
global_stream_lock.lock();
std::cout << "[" << boost::this_thread::get_id()
<< "] data recvd" << std::endl;
global_stream_lock.unlock();
} // RPC::data_recv
void on_data_recv (void) {
...
}
The error code cannot be taken by reference. Make it by-value or by const&:
void on_data_recv(boost::system::error_code/* ec */, size_t /*bytes_transferred*/) {
Also, consider using the Asio specific placeholders:
socket.async_read_some(boost::asio::buffer(recv_buffer),
boost::bind(&RPC::on_data_recv, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
Also use proper lock guards. We're in C++! It's easy to make things exception-safe, so why not?
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <boost/thread.hpp>
static boost::mutex global_stream_lock;
class RPC {
char recv_buffer[56];
public:
void data_recv() {
socket.async_read_some(boost::asio::buffer(recv_buffer),
boost::bind(&RPC::on_data_recv, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
boost::lock_guard<boost::mutex> lk(global_stream_lock);
std::cout << "[" << boost::this_thread::get_id() << "] data recvd" << std::endl;
global_stream_lock.unlock();
}
void on_data_recv(boost::system::error_code/* ec */, size_t /*bytes_transferred*/) {
{
boost::lock_guard<boost::mutex> lk(global_stream_lock);
std::cout << "[" << boost::this_thread::get_id() << "] bytes rcvd: " << std::endl;
}
data_recv(); // call function that waits for more data
}
boost::asio::io_service service;
boost::asio::ip::tcp::socket socket{service};
}; // RPC class def
int main() {}

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