Missing the first characters using boost::asio::streambuf - c++

I make an asynchronous chat server in C++ using the boost library. Almost everything works fine.
There are two ways for a client to disconnect:
by pressing Ctrl + C (killing the client process)
by entering "exit".
The former is OK. However, the latter has a problem. If a client disconnects with "exit", the next message, sent by another client, appears without the first several characters. After that it's OK.
For example: Several clients chat. One of them disconnects with "exit". After that, another client sends "0123456789abcdefghijk" and all clients receive only: "abcdefghijk". I don't know where's the problem, I guess it's something about streambuf. I found similar problem (almost the same) but in C#.
Here's the code:
#include<iostream>
#include<list>
#include<map>
#include<queue>
#include<vector>
#include<cstdlib>
#include<ctime>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
using namespace std;
using namespace boost::asio;
using namespace boost::asio::ip;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
typedef boost::shared_ptr<string> string_ptr;
typedef boost::shared_ptr< list<socket_ptr> > clientList_ptr;
typedef boost::shared_ptr< list<string> > nameList_ptr;
const string waitingMsg("Waiting for clients...\n");
const string totalClientsMsg("Total clients: ");
const string errorReadingMsg("Error on reading: ");
const string errorWritingMsg("Error on writing: ");
const int EOF_ERROR_CODE = 2;
const int THREADS = 1;
io_service service;
tcp::acceptor acceptor(service, tcp::endpoint(tcp::v4(), 30001));
boost::mutex mtx;
clientList_ptr clientList(new list<socket_ptr>);
nameList_ptr nameList(new list<string>);
boost::asio::streambuf buff;
time_t timer;
void ready();
void accepting();
void askForName(socket_ptr clientSock, const boost::system::error_code& error);
void receiveName(socket_ptr clientSock, const boost::system::error_code& error,
std::size_t bytes_transferred);
void identify(socket_ptr clientSock, const boost::system::error_code& error, std::size_t bytes_transferred);
void accepted(socket_ptr clientSock, string_ptr name);
void receiveMessage(socket_ptr clientSock, string_ptr name);
void received(socket_ptr clientSock, string_ptr name, const boost::system::error_code& error,
std::size_t bytes_transferred);
bool extract(string_ptr message, std::size_t bytes_transferred);
bool clientSentExit(string_ptr clientSock);
void disconnectClient(socket_ptr clientSock, string_ptr name, const boost::system::error_code& error);
void writeMessage(socket_ptr clientSock, string_ptr message);
void responseSent(const boost::system::error_code& error);
void notification(socket_ptr sock, string_ptr name, const string headOfMsg, const string tailOfMsg);
int main(int argc, char* argv[])
{
try
{
vector<boost::shared_ptr<boost::thread> > threads;
ready();
for (int i = 0; i < THREADS; i++)
{
boost::shared_ptr <boost::thread> t(new boost::thread(boost::bind(&io_service::run, &service)));
threads.push_back(t);
}
for (int i = 0; i < THREADS; i++)
{
threads[i]->join();
}
}
catch (std::exception& error)
{
cerr << error.what() << endl;
}
return 0;
}
void ready()
{
cout << waitingMsg;
accepting();
}
void accepting()
{
socket_ptr clientSock(new tcp::socket(service));
acceptor.async_accept(*clientSock, boost::bind(&askForName, clientSock, boost::asio::placeholders::error));
}
void askForName(socket_ptr sock, const boost::system::error_code& error)
{
if (error)
{
cerr << "Error on accepting: " << error.message() << endl;
}
boost::asio::async_write(*sock, buffer("Please, enter your name:\n"),
boost::bind(&receiveName, sock, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
accepting();
}
void receiveName(socket_ptr sock, const boost::system::error_code& error,
std::size_t bytes_transferred)
{
if (error)
{
cerr << errorWritingMsg << error.message() << endl;
}
boost::asio::async_read_until(*sock, buff, '\n',
boost::bind(&identify, sock, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void identify(socket_ptr sock, const boost::system::error_code& error,
std::size_t bytes_transferred)
{
if(error)
{
if (error.value() != EOF_ERROR_CODE)
{
cerr << errorReadingMsg << error.message() << endl;
}
return;
}
string_ptr name(new string(""));
if (!extract(name, bytes_transferred))
{
return;
}
if (find(nameList->begin(), nameList->end(), *name) != nameList->end())
{
boost::asio::async_write(*sock, buffer("This name is already in use! Please, select another name:\n"),
boost::bind(&receiveName, sock, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
return;
}
nameList->emplace_back(*name);
accepted(sock, name);
}
void accepted(socket_ptr sock, string_ptr name)
{
mtx.lock();
clientList->emplace_back(sock);
mtx.unlock();
notification(sock, name, "New client: ", " joined. ");
receiveMessage(sock, name);
}
void receiveMessage(socket_ptr sock, string_ptr name)
{
boost::asio::async_read_until(*sock, buff, '\n', boost::bind(&received, sock, name, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void received(socket_ptr sock, string_ptr name, const boost::system::error_code& error,
std::size_t bytes_transferred)
{
if(error)
{
if (error.value() != EOF_ERROR_CODE)
{
cerr << errorReadingMsg << error.message() << endl;
}
disconnectClient(sock, name, error);
return;
}
if(!clientList->empty())
{
//mtx.lock();
string_ptr message(new string(""));
if(!extract(message, bytes_transferred))
{
//mtx.unlock();
disconnectClient(sock, name, error);
return;
}
*message = *name + ": " + *message + "\n";
cout << "ChatLog: " << *message << endl;
writeMessage(sock, message);
receiveMessage(sock, name);
//mtx.unlock();
}
}
bool extract(string_ptr message, std::size_t bytes_transferred)
{
mtx.lock();
buff.commit(bytes_transferred);
std::istream istrm(&buff);
//mtx.unlock();
std::getline(istrm, *message);
buff.consume(buff.size());
string_ptr msgEndl(new string(*message + "\n"));
mtx.unlock();
if(clientSentExit(msgEndl))
{
return false;
}
return true;
}
bool clientSentExit(string_ptr message)
{
return message->compare(0, 5, "exit\n") == 0;
}
void disconnectClient(socket_ptr sock, string_ptr name, const boost::system::error_code& error)
{
boost::system::error_code ec = error;
auto position = find(clientList->begin(), clientList->end(), sock);
auto namePos = find(nameList->begin(), nameList->end(), *name);
sock->shutdown(tcp::socket::shutdown_both, ec);
if (ec)
{
cerr << "Error on shutting: " << ec.message() << endl;
}
sock->close(ec);
if(ec)
{
cerr << "Error on closing: " << ec.message() << endl;
}
clientList->erase(position);
nameList->erase(namePos);
notification(sock, name, "", " disconnected. ");
}
void writeMessage(socket_ptr sock, string_ptr message)
{
for(auto& cliSock : *clientList)
{
if (cliSock->is_open() && cliSock != sock)
{
boost::asio::async_write(*cliSock, buffer(*message),
boost::bind(&responseSent, boost::asio::placeholders::error));
}
}
}
void responseSent(const boost::system::error_code& error)
{
if (error)
{
cerr << "Error on writing: " << error.message() << endl;
}
}
void notification(socket_ptr sock, string_ptr name, const string headOfMsg, const string tailOfMsg)
{
string_ptr serviceMsg (new string (headOfMsg + *name + tailOfMsg));
cout << *serviceMsg << totalClientsMsg << clientList->size() << endl;
*serviceMsg = *serviceMsg + "\n";
writeMessage(sock, serviceMsg);
cout << waitingMsg;
}
It's interesting that I have similar synchronous server with the same way of using of streambuf, but there are no such problems.

boost::asio::async_read_until() can read any amount of characters to streambuf after \n. It then gives you bytes_transferred, which is count of characters in the first line (not necessarily the count of characters that were read to the buffer). See documentation.
As long as you keep your buffer variable intact, next boost::asio::async_read_until() will read characters first from the buffer and then from the socket.
It seems to me that you read a line from the buffer using getline(), which is correct. After that, you call
buff.consume(buff.size());
which clears the buffer, removing all information about the partial lines you may have received. The first complete line that you read has already been removed from the buffer by getline(), so the consume() call is unnecessary in any case.
Just removing the consume() call would not solve your problem, because you seem to use one buffer that is shared between all clients, and you would not know what partial data was from which client. A possible solution could be creating a list of buffers (one for each client), just like you have a list of sockets. Then boost::asio::async_read_until() and getline() would take care of handling the partial data, and you wouldn't have to think about that.

The other answer explains what went wrong.
However it can be a bit tricky to find out how to fix it.
Maybe you can find inspiration from a similar question I handled here:
Boost asio: Unable to read URL Body (JSON)
Here, the OP was having trouble with basically the same thing: after reading his HTTP headers he would have "dropped" part of the body. So I added logic:
NOTE Again, it's important not to assume that the end-of-headers coincides with the packet boundary. Therefore, start read_body() with draining the available input already received.
std::shared_ptr<std::vector<char> > bufptr = std::make_shared<std::vector<char> >(nbuffer);
auto partial = std::copy(
std::istreambuf_iterator<char>(&pThis->buff), {},
bufptr->begin());
std::size_t already_received = std::distance(bufptr->begin(), partial);
assert(nbuffer >= already_received);
nbuffer -= already_received;

I think I fixed the issue. Just created a list of streambufs - a streambuf for each client. But I had to keep the consume() function, because otherwise the check if a given name already exists failed resulting in possibility to have several clients sharing the same name. Then messaging stopped working but I removed the locks in extract() and now everything appears to be all right.

Related

Boost::Asio C++ Why I`m getting the garbage while reading buffer?

I have a client which read file and sends the data to server line by line. Server must count the number of lines sent. I'm using boost::asio::async_read_until to reach this result. But I'm getting the garbage (like this: Line: �68�) when trying to read data from buffer. Client sends the data only in ASCII encoding.
Client code fragment:
std::ifstream infile(argv[1]);
std::string line;
while (std::getline(infile, line)) {
boost::asio::write(s, boost::asio::buffer(line + '\n'));
std::cout << line << std::endl;
Server read function:
void handle_read(const boost::system::error_code& error, size_t bytes_trasferred, boost::asio::streambuf& buf)
{
if (!error)
{
if (!bytes_trasferred)
{
std::cout << "0 bytes trasferred\n";
return;
}
std::string data = boost::asio::buffer_cast<const char*>(buf.data());
std::istringstream is(data);
std::string line;
std::getline(is, line);
std::cout << "Line: " << line << std::endl;
}
else
std::cerr << "Error: " << error.message() << std::endl;
}
void do_read()
{
std::cout << "do_read\n";
auto self(shared_from_this());
boost::asio::streambuf buf;
buf.prepare(1048576);
std::cout << "ASYNC\n";
boost::asio::async_read_until(socket_, buf, "\n",
boost::bind(&TcpConnection::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(buf)));
}
How to resolve it? Help me, please!
I've tried to use 'self' 'this' instead. It follows to memory leak.
I've tried to add null terminator after getting data in handler function. ChatGPT said me about this way, but behavior still same.
Here:
std::string data = boost::asio::buffer_cast<const char*>(buf.data());
This is Undefined Behaviour as your are assuming that buf.data() points to a character sequence that is properly terminated with a NUL character, where you have absolutely no reason to assume this.
Besides you have UB because you pass a reference to buf, a local variable, which will by definition no longer be valid after do_read returns.
Thirdly, as Alan pointed out, you failed to copy the shared pointer (self) into the bound handler.
You're mixing many different ways to attack the problem of dealing with line-wise input. By far the easiest approach is to
use a dynamic buffer (like indeed, streambuf)
make it a member, because that's why you have shared_from_this in the first place
do not use prepare() because async_read_until knows how to do that (just like it does commit() for you)
do use consume() instead of doing getline /again/, even though async_read_until already tells you where the newline is.
Combining that:
struct TcpConnection : std::enable_shared_from_this<TcpConnection> {
TcpConnection(tcp::socket s) : socket_(std::move(s)) {}
void run() {
do_read();
}
private:
tcp::socket socket_;
asio::streambuf buf;
void handle_read(error_code ec, size_t n) {
std::cerr << "handle_read: " << ec.message() << std::endl;
if (!ec) {
std::string const line(boost::asio::buffer_cast<char const*>(buf.data()), n);
buf.consume(n);
std::cout << "Line: " << quoted(line) << std::endl;
do_read();
}
}
void do_read() {
async_read_until( //
socket_, buf, "\n",
boost::bind(&TcpConnection::handle_read, shared_from_this(), asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
};
I'd probably simplify, avoiding copying and potentially allocations:
struct TcpConnection : std::enable_shared_from_this<TcpConnection> {
TcpConnection(tcp::socket s) : socket_(std::move(s)) {}
void run() {
buf_.reserve(8192); // optional, tune to taste
do_read();
}
private:
tcp::socket socket_;
std::string buf_;
void handle_read(error_code ec, size_t n) {
std::cerr << "handle_read: " << ec.message() << std::endl;
if (!ec) {
auto line = std::string_view(buf_).substr(0, n);
std::cout << "Line: " << quoted(line) << std::endl;
buf_.erase(0, n);
do_read();
}
}
void do_read() {
async_read_until( //
socket_, asio::dynamic_buffer(buf_), "\n",
boost::bind(&TcpConnection::handle_read, shared_from_this(), ph::error,
ph::bytes_transferred));
}
};
This still uses a dynamic buffer, but you don't have to do the extra hoops with istream which you apparently really don't need. For illustration purposes only, you could make the dynamic_string_buffer explicit:
void run() {
backing_.reserve(8192); // optional, tune to taste
do_read();
}
private:
tcp::socket socket_;
std::string backing_;
asio::dynamic_string_buffer<char, std::char_traits<char>, std::allocator<char>> buf_{backing_};
void handle_read(error_code ec, size_t n) {
std::cerr << "handle_read: " << ec.message() << std::endl;
if (!ec) {
auto line = std::string_view(backing_).substr(0, n);
std::cout << "Line: " << quoted(line) << std::endl;
buf_.consume(n); // look mom, it's a DynamiceBuffer all the same!
do_read();
}
}
void do_read() {
async_read_until( //
socket_, buf_, "\n",
boost::bind(&TcpConnection::handle_read, shared_from_this(), ph::error,
ph::bytes_transferred));
}
Note, I wouldn't do that, but it demonstrates you how both approaches use the same DynamicBuffer concept as you did with streambuf.
Live Demo
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
namespace ph = asio::placeholders;
using asio::ip::tcp;
using boost::system::error_code;
struct TcpConnection : std::enable_shared_from_this<TcpConnection> {
TcpConnection(tcp::socket s) : socket_(std::move(s)) {}
void run() {
buf_.reserve(8192); // optional, tune to taste
do_read();
}
private:
tcp::socket socket_;
std::string buf_;
void handle_read(error_code ec, size_t n) {
if (n) {
auto line = std::string_view(buf_).substr(0, n - 1); // exclude '\n'
std::cout << socket_.remote_endpoint() << " Line: " << quoted(line) << std::endl;
buf_.erase(0, n);
}
if (!ec)
do_read();
else
std::cerr << "handle_read: n:" << n << " " << ec.message() << std::endl;
}
void do_read() {
async_read_until( //
socket_, asio::dynamic_buffer(buf_), "\n",
boost::bind(&TcpConnection::handle_read, shared_from_this(), ph::error,
ph::bytes_transferred));
}
};
struct Server {
Server(asio::any_io_executor ex) : acc(ex, {{}, 7878}) {}
void start() {
do_accept();
}
private:
tcp::acceptor acc;
void do_accept() {
acc.async_accept(make_strand(acc.get_executor()), [this](error_code ec, tcp::socket s) {
if (!ec) {
std::make_shared<TcpConnection>(std::move(s))->run();
do_accept();
}
});
}
};
int main() {
asio::io_context ioc;
Server srv(ioc.get_executor());
srv.start();
ioc.run();
}
With the client emulated by a very similar oneliner:
netcat 127.0.0.1 7878 -w0 < main.cpp
Prints:
127.0.0.1:54860 Line: "#include <boost/asio.hpp>"
127.0.0.1:54860 Line: "#include <boost/bind/bind.hpp>"
127.0.0.1:54860 Line: "#include <iomanip>"
127.0.0.1:54860 Line: "#include <iostream>"
127.0.0.1:54860 Line: "namespace asio = boost::asio;"
...
...
127.0.0.1:54860 Line: "int main() {"
127.0.0.1:54860 Line: " asio::io_context ioc;"
127.0.0.1:54860 Line: ""
127.0.0.1:54860 Line: " Server srv(ioc.get_executor());"
127.0.0.1:54860 Line: " srv.start();"
127.0.0.1:54860 Line: ""
127.0.0.1:54860 Line: " ioc.run();"
127.0.0.1:54860 Line: "}"
handle_read: n:0 End of file
Local demo showing multiple concurrent clients:

Boost ASIO, async_read_some callback not called

My code works for read_some, but not for async_read_some. The data I'm reading is 5 chars long, whereas MAX_RESPONSE_SIZE 256. I call async_read_some once from my main after opening the port, but the callback is never called after I swipe my prox card a few times. I have tried adding io_service.run() after async_read_some but it did not help. Am I missing something? Thank you.
header
boost::system::error_code error;
boost::asio::io_service io_service;
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
serial_port_ptr serial_port;
char read_buffer[MAX_RESPONSE_SIZE];
open
serial_port.reset();
serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
serial_port->open(device_path, error);
serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
serial_port->set_option(boost::asio::serial_port_base::character_size(8));
serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
read
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
callback
void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)
{
// do stuff
}
You must ensure that there is always work to do, so that io_service::run() does not return and complete the thread where it is running.
As mentioned in the comments, you can create an io_service::work. However, I consider this artificial, a symptom of a design problem.
The better answer, probably, is that in the data_received handler, you should prepare for the next read if no fatal error occurred
void serial_comm::data_received(
const boost::system::error_code& error,
size_t bytes_transferred)
{
// do stuff
if( any_kind_of_fatal_error )
{
// return without setting up the next read
// this will end reading
return;
}
// the last read was successful
// so setup for the next
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
Basically my problem was not starting the io_service thread after async_read_some in the same function. Can you blame me? This stuff is not very clear cut. Here's my code in case anyone wants it (INFO and ERROR come from boost logging, see one of my other questions on it):
serial_comm.hpp
#ifndef __SERIAL_COMM_HPP
#define __SERIAL_COMM_HPP
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <string>
#include <atomic>
#include "logging.hpp" // Boost logging
#define MAX_RESPONSE_SIZE 256
class serial_comm
{
public:
void open_serial_port (std::string device_path, unsigned int baud_rate);
void close_serial_port (void);
void async_read_some (void);
std::string serial_read_data;
std::atomic <bool> serial_data_read_complete{false};
private:
// functions
void data_received (const boost::system::error_code& ec, size_t bytes_transferred);
// variables
boost::mutex mutex;
boost::system::error_code error;
boost::asio::io_service io_service;
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
serial_port_ptr serial_port;
char read_buffer[MAX_RESPONSE_SIZE];
};
#endif // __SERIAL_COMM_HPP
serial_comm.cpp
#include "../include/serial_comm.hpp"
void serial_comm::open_serial_port (std::string device_path, unsigned int baud_rate)
{
INFO << "started";
try
{
serial_port.reset();
serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
serial_port->open(device_path, error);
if (error)
{
ERROR << "error.message() >> " << error.message().c_str();
throw -3;
}
// set options
serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
serial_port->set_option(boost::asio::serial_port_base::character_size(8));
serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
}
catch (int error)
{
ERROR << "error = " << error;
throw -1;
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -2;
}
INFO << device_path << " opened correctly";
INFO << "ended";
return;
}
void serial_comm::close_serial_port()
{
boost::mutex::scoped_lock lock(mutex); // prevent multiple thread access
INFO << "started";
try
{
if (serial_port)
{
serial_port->cancel();
serial_port->close();
serial_port.reset();
}
else
{
WARNING << "serial port is not open";
}
io_service.stop();
io_service.reset();
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
INFO << "ended";
return;
}
void serial_comm::async_read_some (void)
{
boost::mutex::scoped_lock lock (mutex); // prevent multiple threads
INFO << "started";
std::string data;
try
{
if (serial_port.get() == NULL || !serial_port->is_open())
{
WARNING << "serial port is not open";
throw -2;
}
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
// start io_service run thread after giving it work
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
INFO << "ended";
return;
}
void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)
{
boost::mutex::scoped_lock lock(mutex); // prevent multiple thread access
INFO << "started";
try
{
if (serial_port.get() == NULL || !serial_port->is_open())
{
WARNING << "serial port is not open";
throw -2;
}
if (error)
{
ERROR << "error.message() >> " << error.message().c_str();
throw -3;
}
for (unsigned int i = 0; i < bytes_transferred; ++i) {
serial_read_data += read_buffer[i];
}
INFO << "bytes_transferred = " << bytes_transferred << "; serial_read_data = " << serial_read_data;
serial_data_read_complete = true;
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
// prevent io_service from returning due to lack of work
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
INFO << "ended";
return;
}
main.cpp
#include "../include/serial_comm.hpp"
int main(void)
{
serial_comm _serial_comm;
try
{
_serial_comm.open_serial_port("/dev/ttyS0", 9600);
_serial_comm.async_read_some(); // this function will always check for data
loop:
while (!_serial_comm.serial_data_read_complete)
{
sleep(1);
}
INFO << "_serial_comm.serial_read_data = " << _serial_comm.serial_read_data;
_serial_comm.serial_read_data.clear();
_serial_comm.serial_data_read_complete = false;
goto loop;
}
catch (int error)
{
ERROR << "error >> " << error;
return;
}
FATAL << "main ended";
return;
}

Boost asio async operation bad file descriptor

I'm usig boost asio for an IRC bot, and one of my async operation results in a bad file descriptor. I tried to put the socket in a shared_ptr, but I still got the "Bad File Descriptor" error. I don't know whats wrong in it.
Here are the files, I omitted some of the functions from the cpp file. But I you want to read the full file, it's here on my Github.
The error happends in the _read function.
Thanks!
irc.hpp
#ifndef H_IRC
#define H_IRC
#include <vector>
#include <boost/asio.hpp>
#include <boost/tokenizer.hpp>
#include <boost/shared_ptr.hpp>
class Irc
{
public:
Irc(const std::string &server, const std::string &port, const std::function<void()> onConnect);
void connect();
void close();
void user(const std::string &username);
void user(const std::string &username, const std::string &hostname, const std::string &server, const std::string &realname);
void nick(std::string &nickname);
void join(const std::string &chan);
void part(const std::string &chan);
void privmsg(const std::string &to, const std::string &msg);
void command(const std::string &cmd, const std::string &msg);
void command(const std::string &cmd, const std::string &to, const std::string &msg);
void run();
private:
void _read(const boost::system::error_code &error);
void _send(std::string &message);
void _readHandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer);
void _connectHandler(const boost::system::error_code &error);
void _pong(const std::string &ping);
std::string _server;
std::string _port;
std::string _chan;
std::vector<std::function<void (const boost::tokenizer<boost::char_separator<char> >&)>> _readHandlers;
std::function<void()> _onConnect;
boost::asio::streambuf _buffer;
boost::asio::io_service _ios;
boost::shared_ptr<boost::asio::ip::tcp::socket> _socket;
};
#endif
irc.cpp
#include "irc.hpp"
#include <iostream>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
Irc::Irc(const std::string &server, const std::string &port, const std::function<void()> onConnect)
: _server(server), _port(port), _onConnect(onConnect),
_socket(boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(_ios)))
{
// Ping back handler
_readHandlers.push_back([this](const boost::tokenizer<boost::char_separator<char> > &tokenizer) {
std::vector<std::string> tokens(begin(tokenizer), end(tokenizer));
if(tokens[0].compare("PING") == 0)
_pong(tokens[1]);
});
}
void Irc::connect()
{
boost::asio::ip::tcp::resolver resolver(_ios);
boost::asio::ip::tcp::resolver::query query(_server, _port);
boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
boost::asio::ip::tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while(it != end)
{
if(!error)
break;
std::cout << "Connecting to " << _server << " " << _port << std::endl;
boost::asio::async_connect(*_socket, it,
boost::bind(&Irc::_connectHandler, this, error)
);
it++;
if(error)
std::cout << "Error : " << error.message() << std::endl;
}
if(error)
std::cout << "Error connectinf to " << _server << " " << error.message() << std::endl;
else
std::cout << "Connection success" << std::endl;
}
void Irc::close()
{
_socket->close();
_ios.stop();
}
void Irc::run()
{
boost::asio::async_read_until(*_socket, _buffer, "\r\n",
boost::bind(&Irc::_read, this,
boost::asio::placeholders::error
)
);
_ios.run();
}
/*
* Private
*/
void Irc::_read(const boost::system::error_code &error)
{
if(error)
{
std::cerr << "Error in read : " << error.message() << std::endl;
}
else
{
std::string data(buffers_begin(_buffer.data()), buffers_begin(_buffer.data()) + _buffer.size());
std::cout << data << std::endl;
boost::char_separator<char> sep("!#:; ");
boost::tokenizer<boost::char_separator<char> > tokenizer(data, sep);
_readHandler(tokenizer);
boost::asio::async_read_until(*_socket, _buffer, "\r\n",
boost::bind(&Irc::_read, this,
boost::asio::placeholders::error
)
);
}
}
inline void Irc::_send(std::string &message)
{
boost::asio::write(*_socket, boost::asio::buffer(message + "\r\n"));
}
void Irc::_readHandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer)
{
for(auto it : _readHandlers)
it(tokenizer);
}
void Irc::_connectHandler(const boost::system::error_code &error)
{
if(!error)
{
_onConnect();
}
}
connect is never called.
This causes the "bad file handle" error
Further Notes
Suddenly, _send uses synchronous asio::write. Why?
Error handling should probably be added there, too (catch or pass error_code& argument).
There's only one socket which never gets re-initialized or assigned. Embedding it into a shared pointer isn't changing anything¹.
This however is strange:
std::cout << "Connecting to " << _server << " " << _port << std::endl;
boost::asio::async_connect(*_socket, it,
boost::bind(&Irc::_connectHandler, this, error)
);
This does potentially many asynchronous connect operations on the same socket simultaneously. This is a data race and therefore Undefined Behaviour, see: documentation.
So you need to fix it to use several sockets or sequential.
Turns out, this is very simple: you're already using the free function version of async_connect:
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect/overload1.html
This function attempts to connect a socket to one of a sequence of endpoints. It does this by repeated calls to the socket's async_connect member function, once for each endpoint in the sequence, until a connection is successfully established.
So the fix is to just call it once.
Your bind doesn't use a placeholder, instead uses a hardcoded error!
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, error));
Needs to be more like
boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, boost::asio::placeholders::error()));
after handling incoming traffic, you need to consume the buffer contents, or it will infinitely repeat the same:
_readHandler(tokenizer);
_buffer.consume(_buffer.size());
Pull Request
https://github.com/Bl4ckb0ne/irc-boost/pull/1
Adds:
869c225 Use shared_ptr
9042c6d Add function level trace
50dee1b Revert shared_ptr and rename _onConnect(ed)
20475b9 Fixing the async_connect debacle
c6d8a2e Fixed channel handling and consistency join/part
6fd9242 Initiate `connect()` instead of read from `run()`
06a6c06 Do **not** assume contiguous buffer storage (UB)
090fe8c Consume handled input
68e5e8a Comment
All the above changed, I have successfully connected to an IRC channel and receiving mesages.
¹ (especially not unless you make sure something hangs on to an instance of the shared_ptr)

Uninitialized read error on Asio tcp socket

I have a c++ windows app which works fine. I use Boost::asio. When I tried porting on linux, the app did not work as intended at all. After getting discouraged by valgrind errors, I decided to run DrMemory on windows and fix the errors that would show up first. One error I have not been able to fix is an error I have deducted to be about my socket. Is it not possible to use scoped_ptr with a socket? Anyways, here are the errors DrMemory records, followed by some relevant code. The code does not compile if I change the logic for the socket from a smart pointer to either references or a bare pointer. (Some error about the socket, I can't recall exactly what it was, but it was the same for reference and pointer)
What have I done wrong with this?
DrMemory : http://pastebin.com/gHQYrCjA
NetworkEntity.cpp
#include "boost/asio.hpp"
#include "NetworkEntity.h"
#include "boost/bind.hpp"
NetworkEntity::NetworkEntity(std::string address, std::string port)
: address_(address),
port_(port)
{
}
void NetworkEntity::stop()
{
if (socket_)
{
socket_->close();
}
socket_.reset();
timeout_->cancel();
}
void NetworkEntity::check_timeout(const boost::system::error_code& error)
{
if (error != boost::asio::error::operation_aborted)
{
stop();
errorbuf_ << "timed out after " << TIMEOUT_ << " seconds\n";
}
}
std::vector<std::string> NetworkEntity::tcpPoll(const char* message, const char endOfMessage)
{
boost::asio::io_service io_service;
try{
//initialize timeout timer.
timeout_.reset(new boost::asio::deadline_timer(io_service));
timeout_->expires_from_now(boost::posix_time::seconds(TIMEOUT_));
timeout_->async_wait(boost::bind(&NetworkEntity::check_timeout, this, boost::asio::placeholders::error));
//initialize connection, which writes then reads.
tcp::resolver resolver(io_service);
start_connect(&io_service, resolver.resolve(tcp::resolver::query(address_, port_)), message, endOfMessage);
//run async operations, wait for their completion.
io_service.run();
//retrieve answer
std::vector<std::string> lines;
std::string line;
std::istream is(&answer_);
int i = 0;
while (std::getline(is, line)){
lines.push_back(line);
}
//reset answer to nothing (not needed but is a security)
answer_.consume(answer_.size());
request_.consume(request_.size());
setError(errorbuf_.str());
errorbuf_.str(""); // clear the contents
errorbuf_.clear();
return lines;
}
catch (std::exception& e){
errorbuf_ << "An exception has occured : " << e.what() << "\n";
return std::vector<std::string>{};
}
}
void NetworkEntity::start_connect(boost::asio::io_service* io_service, tcp::resolver::iterator endpoint_iterator, const std::string message, const char endOfMessage)
{
// Start the asynchronous connect operation.
socket_.reset(new tcp::socket(*io_service));
socket_->async_connect(endpoint_iterator->endpoint(),
boost::bind(&NetworkEntity::handle_connect, this, io_service, boost::asio::placeholders::error, message, endOfMessage));
}
void NetworkEntity::handle_connect(boost::asio::io_service* io_service, const boost::system::error_code& err, const std::string message, const char endOfMessage)
{
if (err)
{
stop();
errorbuf_ << "Connect error : " << err.message() << "\n";
}
else
{
start_write(message, endOfMessage);
}
}
void NetworkEntity::start_write(const std::string message, const char endOfMessage)
{
//convert message from string to streambuf
std::ostream request_stream(&request_);
request_stream << message;
//end of convertion
boost::asio::async_write(*socket_, request_,
boost::bind(&NetworkEntity::handle_write, this, boost::asio::placeholders::error, endOfMessage));
}
void NetworkEntity::handle_write(const boost::system::error_code& error, const char endOfMessage)
{
if (!error)
{
boost::asio::io_service io;
boost::asio::deadline_timer wait(io);
wait.expires_from_now(boost::posix_time::milliseconds(500));
wait.wait();
start_read(endOfMessage);
}
else
{
stop();
errorbuf_ << "Write error : " << error.message() << "\n";
}
}
void NetworkEntity::start_read(const char endOfMessage)
{
boost::asio::async_read_until(*socket_, answer_, endOfMessage,
boost::bind(&NetworkEntity::handle_read, this, boost::asio::placeholders::error));
}
void NetworkEntity::handle_read(const boost::system::error_code& error)
{
if (error)
{
errorbuf_ << "read error : " << error.message() << "\n";
}
stop();
}
NetworkEntity.h
#inclued "boost/asio.hpp"
#include "boost/array.hpp"
#include "boost/scoped_ptr.hpp"
#include "boost/shared_ptr.hpp"
#include "Error.h"
#ifndef NETWORK_ENTITY_H
#define NETWORK_ENTITY_H
using boost::asio::ip::tcp;
class NetworkEntity : private boost::noncopyable
{
public:
NetworkEntity(std::string address, std::string port);
std::vector<std::string> tcpPoll(const char* message, const char endOfMessage);
private:
void stop();
void check_timeout(const boost::system::error_code& error);
void start_write(const std::string message, const char endOfMessage);
void handle_write(const boost::system::error_code& error, const char endOfMessage);
void start_read(const char endOfMessage);
void start_connect(boost::asio::io_service* io_service, tcp::resolver::iterator endpoint_iterator, const std::string message, const char endOfMessage);
void handle_connect(boost::asio::io_service* io_service, const boost::system::error_code& error, const std::string message, const char endOfMessage);
void handle_read(const boost::system::error_code& error);
void timeoutHandler(const boost::system::error_code& error);
boost::scoped_ptr<tcp::socket> socket_;
boost::scoped_ptr<boost::asio::deadline_timer> timeout_;
std::string address_;
std::string port_;
boost::asio::streambuf answer_;
boost::asio::streambuf request_;
static const int TIMEOUT_ = 5;
std::stringstream errorbuf_; //loggable by functions I removed for the sake of simplicity
boost::shared_ptr<Error> error_;
boost::scoped_ptr<boost::asio::deadline_timer> logTimer_;
NetworkEntity(const NetworkEntity& e) = delete;
NetworkEntity & operator=(const NetworkEntity& e) = delete;
};
#endif
main.cpp (I did not try to compile this one, sorry if there are any errors)
#include "NetworkEntity.h"
#include <iostream>
int main()
{
NetworkEntity n("192.168.0.36", "10001");
while (true){
std::string mes;
std::cin >> mes;
std::vector<std::string> ans = n.tcpPoll(mes.c_str(), '\n'); //message to send, last character expected to recieve (read until)
for (int i = 0; i < ans.size(); i++)
std::cout << ans[i] << "\n";
}
}
Some tips to help you to find the problem, hopefully:
move boost::asio::io_service io_service from NetworkEntity::tcpPoll to class member variable.
I don't undertsand why you create another ioservice in NetworkEntity::handle_write, use the member variable.
In NetworkEntity::tcpPoll, 'timeout_.reset(new boost::asio::deadline_timer(io_service))' will create a memory leak, use std::make_shared
I do not understand the error handling in NetworkEntity::handle_write as you 'reset' the socket, and continue the io_service.run() in NetworkEntity::tcpPoll without even ckecking kind of error.
And last but not least, about your question 'Is it not possible to use scoped_ptr with a socket?', socket is one thing, pointer, scoped or not another thing. You should read and learn about smart pointer, nothing magic behind that term, only a reference count, and delete when that count reach 0.
And as bonus a free advice: forget DrMemory and understand exactly each line, each statement of your code, if function has a returned value, get it and check it ...
Good luck !
In the end, I made it work on linux and run valgrind-clean. My problem wasn't even in what was shown here, it was that I was passing "10001\r" instead of "10001" as the port, which made the resolver fail. That \r came from a getline (read from file) which deleted only the \n in "10001\r\n"
socket_.reset();
should be inside the prior 'if' block.

std::string copy constructor seg fault

this happens when constructing a structure, my code:
http://wklej.org/hash/c42680a7f9d/txt/ at this line: http://wklej.org/hash/5fefcecc371/txt/ backtrace: http://wklej.org/id/451070/txt/
any help is apperciated
Sorry, i fail to copy the code here so i post it at any other site ;(
Use a debugger and get a stack trace.
The problem is almost certainly passing a bad C string into the std::string constructor. Perhaps the pointer is invalid or the C string does not terminate and the constructor reads off into protected memory.
But without more information I can't tell what the bug is. The debugger should point it out immediately.
Also, your Socket holds a pointer but only defines a constructor and destructor. You also need a copy constructor and an assignment operator. If those two operations aren't supposed to happen then define them as private with no implementation.
Also, I see from your backtrace that this is an old version of GCC. It's possible that this version does not have the fixes that make std::string safe to use in multi-threaded programs. I don't know when it was fixed but some older versions of the libstdc++ library didn't lock the reference counts on the string and could crash when different threads would free the string memory while also writing to it.
I've placed your code here in order to be able to edit it:
#ifdef _WIN32
#define _WIN32_WINNT 0x0501
#endif
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <sstream>
#include <string>
#ifdef assert
#undef assert
#endif
#define assert(x)\
if (!x) { \
std::clog << "[Error - __FUNCTION__] Asseration to: " << #x << " failed. line: " << __LINE__ << ", file: " << __FILE__ << "." << std::endl;\
std::exit(100);\
}
template<typename _Tp>
inline std::string toString(_Tp __p)
{
std::stringstream ss;
ss << __p;
std::string ret;
ss >> ret;
return ret;
}
struct Proxy;
typedef std::vector<Proxy*> ProxyVec;
struct Proxy
{
std::string name;
uint32_t port;
};
ProxyVec loadProxies(const std::string& fileName)
{
std::FILE *f = fopen(fileName.c_str(), "r");
if (!f) {
std::clog << "[Error - loadProxies] Cannot open: " << fileName << "." << std::endl;
delete f;
f = NULL;
return ProxyVec();
}
char buffer[1024];
ProxyVec ret;
int32_t __n = 0;
while (fgets(buffer, sizeof(buffer), f)) {
++__n;
std::string str(buffer);
if (str.find("\n") != std::string::npos)
str = str.substr(0, str.length()-1);
if (str.find("\r") != std::string::npos)
str = str.substr(0, str.length()-1);
size_t sep = str.rfind(":");
if (sep == std::string::npos) {
std::clog << "[Error - loadProxies] Cannot load proxy #" << __n << "." << std::endl;
continue;
}
std::string hostname = str.substr(0, sep);
uint32_t port = static_cast<uint32_t>(std::atoi(str.substr(sep+1, str.length()-sep).c_str()));
std::clog << "Loading proxy: " << hostname << ":" << port << "." << std::endl;
Proxy proxy;
proxy.name = hostname;
proxy.port = port;
ret.push_back(&proxy);
}
std::clog << "Loaded: " << __n << " proxies." << std::endl;
return ret;
}
class Socket
{
public:
Socket(boost::asio::io_service& service, const std::string& host,
const std::string& port, const std::string& targetHost, const std::string& targetPort);
~Socket();
void write(const std::string& message);
std::string receivedData() const;
bool connected() const;
void receive();
void handle();
private:
struct SocketData
{
SocketData(boost::asio::io_service& service, const std::string& _host, const std::string& _port,
const std::string& _targetHost, const std::string& _targetPort):
socket(service),
write(service),
read(service),
resolver(service),
buffers(),
host(_host),
port(_port),
targetHost(_targetHost),
targetPort(_targetPort),
connected(false)
{
}
boost::asio::ip::tcp::socket socket;
boost::asio::io_service::strand write, read;
boost::asio::ip::tcp::resolver resolver;
boost::array<char, 1024> buffers;
std::string host, port;
std::string targetHost, targetPort;
bool connected;
};
// FIXME: Use shared_ptr instead.
SocketData* d;
protected:
//handle resolve func
void handle_resolve(const boost::system::error_code&,
boost::asio::ip::tcp::resolver::iterator);
//handle connection func
void handle_connect(const boost::system::error_code&,
boost::asio::ip::tcp::resolver::iterator);
//handle write func
void handle_write(const boost::system::error_code&, size_t);
//handle read func
void handle_read(const boost::system::error_code&, size_t);
private:
void connectionThread();
};
Socket::Socket(boost::asio::io_service& service, const std::string& host, const std::string& port,
const std::string& targetHost, const std::string& targetPort)
: d(new SocketData(service, host, port, targetHost, targetPort))
{
boost::thread thread(boost::bind(&Socket::connectionThread, this));
// FIXME: This function is blocking. The constructur will never exit.
// Use thread_group.join_all() as last line in main() instead.
thread.join();
}
Socket::~Socket()
{
d->socket.close();
delete d;
d = NULL;
}
void Socket::connectionThread()
{
if (!d)
return;
if (d->connected)
return;
boost::asio::ip::tcp::resolver::query query(d->host, d->port);
d->resolver.async_resolve(query,
boost::bind(&Socket::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
void Socket::handle_resolve(const boost::system::error_code& e,
boost::asio::ip::tcp::resolver::iterator ep_iter)
{
if (!e) {
boost::asio::ip::tcp::endpoint iter = *ep_iter;
d->socket.async_connect(iter,
boost::bind(&Socket::handle_connect, this,
boost::asio::placeholders::error, ++ep_iter));
} else {
std::clog << "[Error - Socket::handle_resolve] " << e.message() << "." << std::endl;
}
}
void Socket::handle_connect(const boost::system::error_code& e,
boost::asio::ip::tcp::resolver::iterator ep_iter)
{
if (!e) {
std::cout << "[Notice - Socket::handle_connect] Connected to host." << std::endl;
d->connected = true;
write("CONNECT " + d->targetHost + ":" + d->targetPort + " HTTP/1.1\r\n\r\n");
receive();
} else if (ep_iter != boost::asio::ip::tcp::resolver::iterator()) {
d->socket.close();
boost::asio::ip::tcp::endpoint ep = *ep_iter;
d->socket.async_connect(ep,
boost::bind(&Socket::handle_connect, this,
boost::asio::placeholders::error, ++ep_iter));
} else {
std::clog << "[Error - Server::handle_connect] " << e.message() << "." << std::endl;
}
}
void Socket::handle_write(const boost::system::error_code& e,
size_t bytes)
{
assert(!e || bytes < 0);
}
void Socket::handle_read(const boost::system::error_code& e,
size_t bytes)
{
assert(!e || bytes < 0);
std::cout << receivedData() << std::endl;
receive();
}
void Socket::write(const std::string& message)
{
boost::asio::async_write(d->socket, boost::asio::buffer(message),
d->write.wrap(
boost::bind(&Socket::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)));
}
std::string Socket::receivedData() const
{
return std::string(d->buffers.data());
}
void Socket::receive()
{
d->socket.async_read_some(boost::asio::buffer(d->buffers),
d->read.wrap(
boost::bind(&Socket::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)));
}
void Socket::handle()
{
assert(!d->targetHost.empty());
assert(!d->targetPort.empty());
std::string str(d->buffers.data());
std::clog << "Received: " << str << "." << std::endl;
if (str.substr(0, 4) == "PING")
write("PO" + str.substr(2) + "\r\n");
else if (str.find("MODE") != std::string::npos)
write("JOIN #OTland\r\n");
else if (str.find("JOIN") != std::string::npos)
write("PRIVMSG #OTland :Hello\r\n");
}
bool Socket::connected() const
{
return d->connected;
}
void handler(const std::string& fileName, const std::string& host, const std::string& port, uint32_t threads);
int main(int argc, char **argv)
{
if (argc < 5) {
std::clog << "[Error - main] Usage: " << argv[0] << " <proxy_file> <host> <port> <threads>" << std::endl;
return 1;
}
std::string file(argv[1]);
std::string host(argv[2]);
std::string port(argv[3]);
uint32_t threads = static_cast<uint32_t>(std::atoi(argv[4]));
if (!threads)
threads = 1;
for (uint32_t __i = 0; __i < threads; ++__i)
// FIXME: Use the thread.join() there.
handler(file, host, port, threads);
}
typedef std::vector<Socket*> SocketVec;
void handler(const std::string& fileName, const std::string& host,
const std::string& port, uint32_t threads)
{
assert(!fileName.empty());
assert(!host.empty());
assert(!port.empty());
ProxyVec proxies = loadProxies(fileName);
assert(proxies.size());
SocketVec sockets;
for (ProxyVec::const_iterator it = proxies.begin(); it != proxies.end(); ++it) {
boost::asio::io_service io;
Socket socket(io, (*it)->name, toString((*it)->port), host, port);
// FIXME: socket is a local variable and it's address is invalid outside
// this loop -> memory leak -> seg-fault.
sockets.push_back(&socket);
}
for (SocketVec::const_iterator it = sockets.begin(); it != sockets.end(); ++it) {
if (!(*it)->connected())
continue;
(*it)->handle();
}
// FIXME: I'm not sure if I understand this architecture. A new thread is
// started with this function as handler ? Who waits until this new created
// thread is finished ? Where is the join() ?
(void) new boost::thread(boost::bind(handler, fileName, host, port, threads));
}
I don't think this is your only problem, but in this snippet in handler, you're creating your Socket objects on the stack. Each Socket object that you create will be destroyed at the end of the for loop. This means that the objects in the sockets vector are invalid objects. Doing this may also corrupt the memory heap enough to produce the error that you're seeing.
SocketVec sockets;
for (ProxyVec::const_iterator it = proxies.begin(); it != proxies.end(); ++it) {
boost::asio::io_service io;
Socket socket(io, (*it)->name, toString((*it)->port), host, port);
sockets.push_back(&socket);
}
Change this to:
SocketVec sockets;
for (ProxyVec::const_iterator it = proxies.begin(); it != proxies.end(); ++it) {
boost::asio::io_service io;
Socket* socket = new Socket(io, (*it)->name, toString((*it)->port), host, port);
sockets.push_back(socket);
}
This answer was originally a comment to Dan's answer, but after looking at your code I felt compelled to give a full answer. You really need to take a closer look at the Boost.Asio examples and understand how they work. Pay special attention to the asynchronous examples, it doesn't look like you've grasped the concepts of object lifetime and how the handlers work. Particularly, you should master single threaded programs before jumping into multiple threads. When you've conquered that, you should use a thread pool invoking io_service::run instead of an io_service per thread. It will ultimately make your program's logic easier to understand.
You also should look into valgrind, there's a slew of errors in your code like this one:
==19853== Invalid read of size 4
==19853== at 0x10000D0E4: handler(std::string const&, std::string const&, std::string const&, unsigned int) (in ./a.out)
==19853== by 0x10000D5E6: main (in ./a.out)
==19853== Address 0x7fff5fbff398 is not stack'd, malloc'd or (recently) free'd