boost::asio::async_read_until problem - c++

I'm trying to modify the echo server example from boost asio and I'm running into problem when I try to use boost::asio::async_read_until. Here's the code:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(socket_, boost::asio::buffer(data_, max_length), ' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
std::cout<<"handling read"<<std::endl;
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
/*
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
*/ }
else
{
delete this;
}
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
The problem is when I try to compile I get this weird error:
server.cpp: In member function ‘void session::start()’:
server.cpp:27: error: no matching function for call to ‘async_read_until(boost::asio::basic_stream_socket >&, boost::asio::mutable_buffers_1, char, boost::_bi::bind_t, boost::_bi::list3, boost::arg<1> ()(), boost::arg<2> ()()> >)’
Can someone please explain what's going on? From what I can tell the arguments to async_read_until are correct.
Thanks!

The second argument of async_read_until should be a streambuf object into which the data will be read. To put it simple, you need to pass a boost::asio::streambuf by reference, not a boost::asio::buffer by value.

There is no need to use streambufs. There are overloads that accept dynamic buffers. Dynamic is key since read_until implies the need for a buffer that can increase its size at the callee's discretion. Therefore, all you need is to replace the call to boost::asio::buffer with boost::asio::dynamic_buffer and call it on either std::string or std::vector<char>.
...
void start()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(
socket_, boost::asio::dynamic_buffer(data_), ' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
...
private:
tcp::socket socket_;
std::string data_;
// or
//std::vector<char> data_;
};
I would also get rid of boost::bind since it's an overkill and use a lambda (use them as much as you can)
[this](const boost::system::error_code& error, size_t bytes_transferred) {
handle_read(error, bytes_transferred);
}

const (in)correctness may also cause this type of error message.

Related

Retrieving adress from browser for my proxy in cpp (boost::Asio) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm trying to adapt a sample code of a TCP Proxy in order to use it on my browser.
Here is the complete proxy i use :
#include <cstdlib>
#include <cstddef>
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
namespace tcp_proxy
{
namespace ip = boost::asio::ip;
class bridge : public boost::enable_shared_from_this<bridge>
{
public:
typedef ip::tcp::socket socket_type;
typedef boost::shared_ptr<bridge> ptr_type;
bridge(boost::asio::io_service& ios)
: downstream_socket_(ios),
upstream_socket_(ios)
{}
socket_type& downstream_socket()
{
return downstream_socket_;
}
socket_type& upstream_socket()
{
return upstream_socket_;
}
void start(const std::string& upstream_host, unsigned short upstream_port)
{
upstream_socket_.async_connect(
ip::tcp::endpoint(
boost::asio::ip::address::from_string(upstream_host),
upstream_port),
boost::bind(&bridge::handle_upstream_connect,
shared_from_this(),
boost::asio::placeholders::error));
}
void handle_upstream_connect(const boost::system::error_code& error)
{
if (!error)
{
upstream_socket_.async_read_some(
boost::asio::buffer(upstream_data_,max_data_length),
boost::bind(&bridge::handle_upstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
downstream_socket_.async_read_some(
boost::asio::buffer(downstream_data_,max_data_length),
boost::bind(&bridge::handle_downstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
private:
void handle_downstream_write(const boost::system::error_code& error)
{
if (!error)
{
upstream_socket_.async_read_some(
boost::asio::buffer(upstream_data_,max_data_length),
boost::bind(&bridge::handle_upstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
void handle_downstream_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(upstream_socket_,
boost::asio::buffer(downstream_data_,bytes_transferred),
boost::bind(&bridge::handle_upstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
void handle_upstream_write(const boost::system::error_code& error)
{
if (!error)
{
downstream_socket_.async_read_some(
boost::asio::buffer(downstream_data_,max_data_length),
boost::bind(&bridge::handle_downstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
void handle_upstream_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(downstream_socket_,
boost::asio::buffer(upstream_data_,bytes_transferred),
boost::bind(&bridge::handle_downstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
void close()
{
boost::mutex::scoped_lock lock(mutex_);
if (downstream_socket_.is_open())
{
downstream_socket_.close();
}
if (upstream_socket_.is_open())
{
upstream_socket_.close();
}
}
socket_type downstream_socket_;
socket_type upstream_socket_;
enum { max_data_length = 8192 }; //8KB
unsigned char downstream_data_[max_data_length];
unsigned char upstream_data_[max_data_length];
boost::mutex mutex_;
public:
class acceptor
{
public:
acceptor(boost::asio::io_service& io_service,
const std::string& local_host, unsigned short local_port,
const std::string& upstream_host, unsigned short upstream_port)
: io_service_(io_service),
localhost_address(boost::asio::ip::address_v4::from_string(local_host)),
acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)),
upstream_port_(upstream_port),
upstream_host_(upstream_host)
{}
bool accept_connections()
{
try
{
session_ = boost::shared_ptr<bridge>(new bridge(io_service_));
acceptor_.async_accept(session_->downstream_socket(),
boost::bind(&acceptor::handle_accept,
this,
boost::asio::placeholders::error));
}
catch(std::exception& e)
{
std::cerr << "acceptor exception: " << e.what() << std::endl;
return false;
}
return true;
}
private:
void handle_accept(const boost::system::error_code& error)
{
if (!error)
{
session_->start(upstream_host_,upstream_port_);
if (!accept_connections())
{
std::cerr << "Failure during call to accept." << std::endl;
}
}
else
{
std::cerr << "Error: " << error.message() << std::endl;
}
}
boost::asio::io_service& io_service_;
ip::address_v4 localhost_address;
ip::tcp::acceptor acceptor_;
ptr_type session_;
unsigned short upstream_port_;
std::string upstream_host_;
};
};
}
int main(int argc, char* argv[])
{
if (argc != 5)
{
std::cerr << "usage: tcpproxy_server <local host ip> <local port> <forward host ip> <forward port>" << std::endl;
return 1;
}
const unsigned short local_port = static_cast<unsigned short>(::atoi(argv[2]));
const unsigned short forward_port = static_cast<unsigned short>(::atoi(argv[4]));
const std::string local_host = argv[1];
const std::string forward_host = argv[3];
boost::asio::io_service ios;
try
{
tcp_proxy::bridge::acceptor acceptor(ios,
local_host, local_port,
forward_host, forward_port);
acceptor.accept_connections();
ios.run();
}
catch(std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Actually, this sample code need as args the local host and local port, and the forward port and the forward host.
I would like to edit this code in order to get automatically the host who is asked in the browser...
From my research i have to used the remote_endpoint() property.
The problem is i don't know how to implement it in order to "trigger" this function eachtime use ask for another adress on the browser...
I tried this :
#include <cstdlib>
#include <cstddef>
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
namespace tcp_proxy
{
namespace ip = boost::asio::ip;
class bridge : public boost::enable_shared_from_this<bridge>
{
public:
typedef ip::tcp::socket socket_type;
typedef boost::shared_ptr<bridge> ptr_type;
bridge(boost::asio::io_service& ios)
: downstream_socket_(ios),
upstream_socket_(ios)
{}
socket_type& downstream_socket()
{
return downstream_socket_;
}
socket_type& upstream_socket()
{
return upstream_socket_;
}
void start(const std::string& upstream_host, unsigned short upstream_port)
{
upstream_socket_.async_connect(
ip::tcp::endpoint(
boost::asio::ip::address::from_string(upstream_host),
upstream_port),
boost::bind(&bridge::handle_upstream_connect,
shared_from_this(),
boost::asio::placeholders::error));
}
void handle_upstream_connect(const boost::system::error_code& error)
{
if (!error)
{
upstream_socket_.async_read_some(
boost::asio::buffer(upstream_data_, max_data_length),
boost::bind(&bridge::handle_upstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
downstream_socket_.async_read_some(
boost::asio::buffer(downstream_data_, max_data_length),
boost::bind(&bridge::handle_downstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
private:
void handle_downstream_write(const boost::system::error_code& error)
{
if (!error)
{
upstream_socket_.async_read_some(
boost::asio::buffer(upstream_data_, max_data_length),
boost::bind(&bridge::handle_upstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
void handle_downstream_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(upstream_socket_,
boost::asio::buffer(downstream_data_, bytes_transferred),
boost::bind(&bridge::handle_upstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
void handle_upstream_write(const boost::system::error_code& error)
{
if (!error)
{
downstream_socket_.async_read_some(
boost::asio::buffer(downstream_data_, max_data_length),
boost::bind(&bridge::handle_downstream_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
close();
}
void handle_upstream_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(downstream_socket_,
boost::asio::buffer(upstream_data_, bytes_transferred),
boost::bind(&bridge::handle_downstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
void close()
{
boost::mutex::scoped_lock lock(mutex_);
if (downstream_socket_.is_open())
{
downstream_socket_.close();
}
if (upstream_socket_.is_open())
{
upstream_socket_.close();
}
}
socket_type downstream_socket_;
socket_type upstream_socket_;
enum { max_data_length = 8192 }; //8KB
unsigned char downstream_data_[max_data_length];
unsigned char upstream_data_[max_data_length];
boost::mutex mutex_;
public:
class acceptor
{
public:
acceptor(boost::asio::io_service& io_service,
const std::string& local_host, unsigned short local_port,
const std::string& upstream_host, unsigned short upstream_port)
: io_service_(io_service),
localhost_address(boost::asio::ip::address_v4::from_string(local_host)),
acceptor_(io_service_, ip::tcp::endpoint(localhost_address, local_port)),
upstream_port_(upstream_port),
upstream_host_(upstream_host)
{}
bool accept_connections()
{
try
{
session_ = boost::shared_ptr<bridge>(new bridge(io_service_));
acceptor_.async_accept(session_->downstream_socket(),
boost::bind(&acceptor::handle_accept,
this,
boost::asio::placeholders::error));
}
catch (std::exception& e)
{
std::cerr << "acceptor exception: " << e.what() << std::endl;
return false;
}
return true;
}
private:
void handle_accept(const boost::system::error_code& error)
{
if (!error)
{
session_->start(upstream_host_, upstream_port_);
if (!accept_connections())
{
std::cerr << "Failure during call to accept." << std::endl;
}
}
else
{
std::cerr << "Error: " << error.message() << std::endl;
}
}
boost::asio::io_service& io_service_;
ip::address_v4 localhost_address;
ip::tcp::acceptor acceptor_;
ptr_type session_;
unsigned short upstream_port_;
std::string upstream_host_;
};
};
}
int main(int argc, char* argv[])
{
if (argc != 5)
{
std::cerr << "usage: EvilProxy.exe <local host ip> <local port> <forward host ip> <forward port>" << std::endl;
return 1;
}
const unsigned short local_port = static_cast<unsigned short>(::atoi(argv[2]));
const unsigned short forward_port = static_cast<unsigned short>(::atoi(argv[4]));
const std::string local_host = argv[1];
const std::string forward_host = argv[3];
boost::asio::io_service ios;
boost::system::error_code ec;
boost::asio::ip::tcp::socket &mySocket = tcp_proxy::bridge(ios).downstream_socket();
try
{
std::string str_IpDest = mySocket.remote_endpoint().address().to_string(); // Here i try to get the adresse but with this code, the adresse is seeking only at the begining...it's too earl
tcp_proxy::bridge::acceptor acceptor(ios,
local_host, local_port,
str_IpDest, forward_port);
acceptor.accept_connections();
ios.run();
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
I'm doing this
std::string str_IpDest = mySocket.remote_endpoint().address().to_string();
But it's called to earl, i don't know how to trigger it each time user ask another adress.
I use boost:asio.
Anyon can help me please ?
The goal...After is to use SSL...but...after :)
Thanks a lot :)
Regards
You need some kind of protocol for communication between your browser and proxy. First your browser needs to send something like "connect to" message, then proxy should send info if connection was successful. I believe that HTTP has some headers specific for proxy handling.

boost::asio usage in self-contained class

Trying to get my head around the boost classes. What I want to do, differs from the boost asio tutorial in that, the tutorial has a main() where the io_service object is instantiated. That is then passed to the class implementing asio via its constructor.
What I want to do, is to eliminate the instantiation of io_service in the main and have the implementing class be "self-contained" in that, it will declare its own io_service and socket. I must be reading the example with tunnel-vision, because I cannot figure out how to drop the instantiation of io_service into my socket class.
At first, I was getting "error C2758: 'xxx::io_service' : must be initialized in constructor base/member initializer list". So, I thought I'd do that and added "io_service(new boost::asio::io_service()), socket(io_service)" to the class initializer list. That gave me "error C2354: 'xxx::io_service' : initialization of reference member requires a temporary variable", which, after some googling, made sense.
So, my question is, how can I adjust the tutorial code to eliminate the main()?
Is this what you're after:
Move the io_service into the class (not by reference). And all usages as well.
I opted to put the join into the destructor.
#include <cstdlib>
#include <deque>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include "chat_message.hpp"
using boost::asio::ip::tcp;
typedef std::deque<chat_message> chat_message_queue;
class chat_client
{
public:
chat_client(std::string host, std::string portorservice)
: io_service_(),
thread_(boost::bind(&boost::asio::io_service::run, &io_service_)),
socket_(io_service_)
{
tcp::resolver resolver(io_service_);
tcp::resolver::query query(host, portorservice);
tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::async_connect(socket_, iterator,
boost::bind(&chat_client::handle_connect, this,
boost::asio::placeholders::error));
}
void write(const chat_message& msg)
{
io_service_.post(boost::bind(&chat_client::do_write, this, msg));
}
void close()
{
io_service_.post(boost::bind(&chat_client::do_close, this));
}
~chat_client() {
close();
if (thread_.joinable())
thread_.join();
}
private:
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
}
void handle_read_header(const boost::system::error_code& error)
{
if (!error && read_msg_.decode_header())
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
boost::bind(&chat_client::handle_read_body, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void handle_read_body(const boost::system::error_code& error)
{
if (!error)
{
std::cout.write(read_msg_.body(), read_msg_.body_length());
std::cout << "\n";
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void do_write(chat_message msg)
{
bool write_in_progress = !write_msgs_.empty();
write_msgs_.push_back(msg);
if (!write_in_progress)
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
write_msgs_.pop_front();
if (!write_msgs_.empty())
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
else
{
do_close();
}
}
void do_close()
{
socket_.close();
}
private:
boost::asio::io_service io_service_;
boost::thread thread_;
tcp::socket socket_;
chat_message read_msg_;
chat_message_queue write_msgs_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: chat_client <host> <port>\n";
return 1;
}
chat_client c(argv[1], argv[2]);
char line[chat_message::max_body_length + 1];
while (std::cin.getline(line, chat_message::max_body_length + 1))
{
using namespace std; // For strlen and memcpy.
chat_message msg;
msg.body_length(strlen(line));
memcpy(msg.body(), line, msg.body_length());
msg.encode_header();
c.write(msg);
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}

Boost asio TCP async server not async?

I am using the code provided in the Boost example.
The server only accepts 1 connection at a time. This means, no new connections until the current one is closed.
How to make the above code accept unlimited connections at the same time?
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(10000));//sleep some time
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
if (!ec)
{
std::make_shared<session>(std::move(socket_))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
server s(io_service, std::atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
As you see, the program waits for the sleep and it doesn't grab a second connection in the meantime.
You're doing a synchronous wait inside the handler which runs on the only thread that serves your io_service. This makes Asio wait with invoking the handlers for any new requests.
Use a deadline_time with wait_async, or,
void do_read() {
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
timer_.expires_from_now(boost::posix_time::seconds(1));
timer_.async_wait([this, self, length](boost::system::error_code ec) {
if (!ec)
do_write(length);
});
}
});
}
Where the timer_ field is a boost::asio::deadline_timer member of session
as a poor-man's solution add more threads (this simply means that if more requests arrive at the same time than there are threads to handle them, it will still block until the first thread becomes available to pick up the new request)
boost::thread_group tg;
for (int i=0; i < 10; ++i)
tg.create_thread([&]{ io_service.run(); });
tg.join_all();
Both the original code and the modified code are asynchronous and accept multiple connections. As can be seen in the following snippet, the async_accept operation's AcceptHandler initiates another async_accept operation, forming an asynchronous loop:
.-----------------------------------.
V |
void server::do_accept() |
{ |
acceptor_.async_accept(..., |
[this](boost::system::error_code ec) |
{ |
// ... |
do_accept(); ----------------------'
});
}
The sleep() within the session's ReadHandler causes the one thread running the io_service to block until the sleep completes. Hence, the program will be doing nothing. However, this does not cause any outstanding operations to be cancelled. For a better understanding of asynchronous operations and io_service, consider reading this answer.
Here is an example demonstrating the server handling multiple connections. It spawns off a thread that creates 5 client sockets and connects them to the server.
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
~session()
{
std::cout << "session ended" << std::endl;
}
void start()
{
std::cout << "session started" << std::endl;
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
if (!ec)
{
std::make_shared<session>(std::move(socket_))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
auto port = std::atoi(argv[1]);
server s(io_service, port);
boost::thread client_main(
[&io_service, port]
{
tcp::endpoint server_endpoint(
boost::asio::ip::address_v4::loopback(), port);
// Create and connect 5 clients to the server.
std::vector<std::shared_ptr<tcp::socket>> clients;
for (auto i = 0; i < 5; ++i)
{
auto client = std::make_shared<tcp::socket>(
std::ref(io_service));
client->connect(server_endpoint);
clients.push_back(client);
}
// Wait 2 seconds before destroying all clients.
boost::this_thread::sleep(boost::posix_time::seconds(2));
});
io_service.run();
client_main.join();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
The output:
session started
session started
session started
session started
session started
session ended
session ended
session ended
session ended
session ended

async_read blocked by async_accept? (boost::asio)

Reads are not handled until after another client connects. This is baffling me, it seems straightforward enough. It appears as if the async_accept is blocking the async_read of all the sessions.
Relevant snippets -
server::server(boost::asio::io_service& io_service, int port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
boost::shared_ptr<session> s(new session(io_service_));
acceptor_.async_accept(s->socket(),
boost::bind(&server::handle_accept, this, s,
boost::asio::placeholders::error));
}
void server::handle_accept(boost::shared_ptr<session> s,
const boost::system::error_code& error)
{
s->start();
boost::shared_ptr<session> s(new session(io_service_));
acceptor_.async_accept(s->socket(),
boost::bind(&server::handle_accept, this, s,
boost::asio::placeholders::error));
}
void session::start()
{
boost::asio::async_read_until(socket_,buffer_,' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void session::handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "handling read.";
boost::asio::async_read_until(socket_,buffer_,' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Am I making some obvious mistake in this basic implementation?

Two-way asynchronous communication in C++

I'm trying to implement two-way asynchronous communication in C++. I'd like to be able to specify the IP address and port number on two machines and be able to get the machines to communicate with each other.
I've looked at Boost::asio and have implemented the following so far:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session{
public:
session(boost::asio::io_service& io_service) : socket_(io_service){
}
tcp::socket& socket(){
return socket_;
}
void start(){
socket_.async_read_some(boost::asio::buffer(data_, max_length),boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
onConnect();
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred){
if (!error){
char* buf = boost::asio::buffer_cast<char*>(boost::asio::buffer(data_, bytes_transferred));
char buf2[bytes_transferred];
int n;
n=sprintf(buf2,"%.*s",bytes_transferred,buf);
onData(buf2);
boost::asio::async_write(socket_, boost::asio::buffer("\0",0), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
}else{
delete this;
}
}
void handle_write(const boost::system::error_code& error){
if (!error){
socket_.async_read_some(boost::asio::buffer(data_, max_length),boost::bind(&session::handle_read, this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}else{
delete this;
}
}
void onConnect(){
printf("Connected\n");
}
void onData(char* buf){
printf("%s",buf);
}
void write(const char* data){
//boost::asio::async_write(socket_, boost::asio::buffer(data, strlen(data)), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
}
private:
tcp::socket socket_;
enum { max_length = 1500 };
char data_[max_length];
};
class server{
public:
server(boost::asio::io_service& io_service, short port) : io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)){
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}
void handle_accept(session* new_session, const boost::system::error_code& error){
if (!error){
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}else{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[]){
try{
if (argc != 2){
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}catch (std::exception& e){
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
I can telnet into this server and send messages to it, but how to access this server programatically from a remote machine? I don't seem to be able to specify an ip address from this code!
I hope someone might have some pointers.
I've not used Boost.ASIO, but searching for "boost asio ip address" and "boost asio gethostbyname" yielded this stuff:
http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/reference/ip__address.html
http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__tcp/resolver.html
The resolver has a resolve method that lets you do things like:
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
boost::asio::ip::tcp::resolver resolver( *io_service );
boost::asio::ip::tcp::resolver::query query(
"www.google.com", // host string
boost::lexical_cast< std::string >( 80 ) // port #
);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query );
boost::asio::ip::tcp::endpoint endpoint = *iterator;
So that'll get you to having a boost::asio::tcp::endpoint which you can use in your socket connection of your client code. The site where I grabbed this is here if you want more details:
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=8
There are plenty of good examples on the Boost.Asio pages to guide you in the right direction.
Simply put, you need to write a server and a client. The former will create the endpoint to connect to and the latter connects. Take a look at the examples. They're very straight-forward and easy to adapt to what your needs.