boost::asio: How do I use async_accept to accept incoming connections? - c++

I'm using boost::asio, and I have code like this:
void CServer::Start(int port)
{
tcp::acceptor acceptor(m_IoService, tcp::endpoint(tcp::v4(), port));
for ( ;; )
{
shared_ptr<tcp::socket> pSocket(new tcp::socket(m_IoService));
acceptor.accept(*pSocket);
HandleRequest(pSocket);
}
}
This code works, but I'd like to switch to using Acceptor::async_accept so that I can call Acceptor::cancel to stop receiving requests.
So my new code looks like this:
void CServer::StartAsync(int port)
{
m_pAcceptor = shared_ptr<tcp::acceptor>( new tcp::acceptor(m_IoService, tcp::endpoint(tcp::v4(), port)) );
StartAccept();
}
void CServer::StopAsync()
{
m_pAcceptor->cancel();
}
void CServer::StartAccept()
{
shared_ptr<tcp::socket> pSocket(new tcp::socket(m_IoService));
m_pAcceptor->async_accept(*pSocket, bind(&CServer::HandleAccept, this, pSocket));
}
void CServer::HandleAccept(shared_ptr<tcp::socket> pSocket)
{
HandleRequest(pSocket);
StartAccept();
}
But this code doesn't seem to work, my function CServer::HandleAccept never gets called. Any ideas? I've looked at sample code, and the main difference between my code and theirs is they seem often make a class like tcp_connection that has the socket as a member, and I'm not seeing why thats necessary.
Alex

Ah, looks like to kick things off you need to run the IOService, e.g.:
m_IoService.run();

Related

boost::asio crash when using a member acceptor instead of new one

I am trying to put the acceptor, socket and endpoint as members into my class but ran into crashes. Must the socket be a shared_ptr like in this Question or why does it not work?
When I'm trying to setup a acceptor on a server like this:
tcp::endpoint ep(boost::asio::ip::address::from_string(localIpAddress), portNumber);
tcp::acceptor a(io_service);
tcp::socket s(io_service);
a.open(ep.protocol());
a.bind(ep);
a.listen(MAX_CONNECTIONS);
a.async_accept(s, boost::bind(&WifiConnector::onAccept, this, boost::asio::placeholders::error));
it runs without crashing during execution, but when I try to use a socket/acceptor/endpoint that are member of my WifiConnector class it crashes.
m_acceptor.open(localEndpoint.protocol()); // it crashes in this line already
m_acceptor.bind(localEndpoint);
m_acceptor.listen(MAX_CONNECTIONS);
m_acceptor.async_accept(socket, boost::bind(&WifiConnector::onAccept, this, boost::asio::placeholders::error));
declaration in WifiConnector.hpp:
private:
tcp::socket m_socket;
tcp::acceptor m_acceptor;
tcp::endpoint m_localEndpoint;
initialization at class constructor:
WifiConnector::WifiConnector() :
io_service(),
m_socket(io_service),
m_acceptor(io_service)
{
m_localIpAddress = "192.168.137.1";
m_portNumber = 30000;
m_localEndpoint = tcp::endpoint(boost::asio::ip::address::from_string(m_localIpAddress), m_portNumber);
}
when it crashes, I get the following exeption:
boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >
private:
tcp::socket m_socket;
tcp::acceptor m_acceptor;
tcp::endpoint m_localEndpoint;
This will not work. You are constructing using the default constructors, which is not what you want. For one thing you want to construct using the io_service used by everything else.
Make the attributes pointers, and construct them using new when you have the io_service.

Server and Client at same time with Boost-Asio

I am an AspNet programmer with 57 years of age. Because I was the only one who worked a little, back in the beginning, with C ++, my bosses asked me to serve a customer who needs a communication agent with very specific characteristics. It can run as a daemon on multiple platforms and be both client and server at times. I do not know enough but I have to solve the problem and found a chance in the Boost / Asio library.
I am new to Boost-Asio and reading the documentation I created a server and a TCP socket client that exchanges messages perfectly and two-way, full duplex.
I read several posts where they asked for the same things I want, but all the answers suggested full duplex as if that meant having a client and a server in the same program. And it's not. The definition of full duplex refers to the ability to write and read from the same connection and every TCP connection is full duplex by default.
I need to make two programs can accept connections initiated by the other. There will be no permanent connection between the two programs. Sometimes one of them will ask for a connection and at other times the other will make this request and both need to be listening, accepting the connection, exchanging some messages and terminating the connection until new request is made.
The server I did seems to get stuck in the process of listening to the port to see if a connection is coming in and I can not continue with the process to be able to create a socket and request a connection with the other program. I need threads but I do not know enough about them.
It'is possible?
As I said I'm new to Boost / Asio and I tried to follow some documents of threads and Coroutines. Then I put the client codes in one method and the server in another.:
int main(int argc, char* argv[])
{
try
{
boost::thread t1(&server_agent);
boost::thread t2(&client_agent);
// wait
t1.join();
t2.join();
return 0;
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
and two Coroutines:
void client_agent() {
parameters param;
param.load();
boost::asio::io_service io_service1;
tcp::resolver resolver(io_service1);
char port[5];
_itoa(param.getNrPortaServComunic(), port, 10);
auto endpoint_iterator = resolver.resolve({ param.getIPServComunicPrincipal(), port });
std::list<client> clients;
client c(io_service1, endpoint_iterator, param);
while (true)
{
BOOL enviada = FALSE;
while (true) {
if (!enviada) {
std::cout << "sending a message\n";
int nr = 110;
message msg(nr, param);
c.write(msg);
enviada = TRUE;
}
}
}
c.close();
}
void server_agent() {
parameters param;
param.load();
boost::asio::io_service io_service1;
std::list<server> servers;
tcp::endpoint endpoint(tcp::v4(), param.getNrPortaAgenteServ());
servers.emplace_back(io_service1, endpoint);
io_service1.run();
}
I used one port to client endpoint and other port to server endpoint. Is it correct? Required?
It starts looking like it's going to work. Each of the methods runs concurrently but then I get a thread allocation error at the io_service1.run (last line of the server_agent method):
boost::exception_detail::clone_impl > at memory location 0x0118C61C.
Any suggestion?
You are describing a UDP client/server application. But your implementation is bound to fail. Think of an asio server or client as always running in a single thread.
The following code is just so you get an idea. I haven't tried to compile it. Client is very similar, but may need a transmit buffer, depends on the app, obviously.
This is a shortened version, so you get the idea. In a final application you way want to add receive timeouts and the likes. The same principles hold for TCP servers, with the added async_listen call. Connected sockets can be stored in shared_ptr, and captured by the lambdas, will destroy almost magically.
Server is basically the same, except there is no constant reading going on. If running both server and client in the same process, you can rely on run() to be looping because of the server, but if not, you'd have to call run() for each connection. run() would exit at the end of the exchange.
using namespace boost::asio; // Or whichever way you like to shorten names
class Server
{
public:
Server(io_service& ios) : ios_(ios) {}
void Start()
{
// create socket
// Start listening
Read();
}
void Read()
{
rxBuffer.resize(1024)
s_.async_receive_from(
buffer(rxBuffer),
remoteEndpoint_,
[this](error_code ec, size_t n)
{
OnReceive(ec, n); // could be virtual, if done this way
});
}
void OnReceive(error_code ec, size_t n)
{
rxBuffer_.resize(n);
if (ec)
{
// error ... stops listen loop
return;
}
// grab data, put in txBuffer_
Read();
s_.async_send_to(
buffer(txBuffer_),
remoteEndpoint_,
[this, msg](error_code ec, size_t n)
{
OnTransmitDone(ec, n);
});
}
void OnTransmitDone(error_code ec, size_t n)
{
// check for error?
txBuffer_.clear();
}
protected:
io_service& ios_;
ip::udp::socket s_;
ip::udp::endpoint remoteEndpoint_; // the other's address/port
std::vector<char> rxBuffer_; // could be any data type you like
std::vector<char> txBuffer_; // idem All access is in one thread, so only
// one needed for simple ask/respond ops.
};
int main()
{
io_service ios;
Server server(ios); // could have both server and client run on same thread
// on same io service this way.
Server.Start();
ios_run();
// or std::thread ioThread([&](){ ios_.run(); });
return 0;
}

Operation canceled boost asio async_receive_from

I have an UDP Server set up with boost/asio (I copied the example and just changed a few things). Below is the code:
udp_server.hpp
using boost::asio::ip::udp;
class udp_server {
public:
udp_server(boost::asio::io_service&, int);
private:
boost::array<char, 256> recBuffer;
udp::socket socket_;
udp::endpoint remote_endpoint_;
void start_receive();
void handle_receive(const boost::system::error_code&, std::size_t);
void handle_send(boost::shared_ptr<std::string> /*message*/,
const boost::system::error_code& /*error*/,
std::size_t /*bytes_transferred*/)
{}
};
and udp_server.cpp
udp_server::udp_server( boost::asio::io_service& io_service,
int port)
: socket_(io_service, udp::endpoint(udp::v4(), port)) {
serverNotifications.push_back("UDP Server class initialized.");
start_receive();
}
void udp_server::start_receive() {
socket_.async_receive_from(
boost::asio::buffer(recBuffer),
remote_endpoint_,
boost::bind(&udp_server::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
serverNotifications.push_back("Starting to receive UDP Messages.");
}
void udp_server::handle_receive(const boost::system::error_code& error,
std::size_t size) {
serverNotifications.push_back("RecFrom: " + remote_endpoint_.address().to_string());
if (!error) {
// I do data stuff here
} else {
errors.push_back("Handle Receive error: " + error.message());
}
}
After initializing the Server with:
try {
udp_server server(io_service, ApplData.PORT, (size_t)ApplData.BUFLEN);
} catch (std::exception& e) {
// error handling
}
and running it with io_service.run() in a seperate try catch in another function I get some problems:
My Callback function handle_receive gets called without any UDP message getting send in the whole network (aka only my laptop without connection)
error.message() returns "Operation canceled"
remote_endpoint_.address().to_string() returns "acfc:4000:0:0:7800::%2885986016" which I can't identify as something useful
Also I recognized that my io_service is stopping all the time, but in my understanding it should run all the time, right?
I already thought about referencing this in the callback function bind with a shared_from_this ptr, but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
Can someone explain thy this failure occurs, what these errors tell me about my code or what I can do to avoid them?
Nevermind, Rubberduck debugging was enough. I just read the line
but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
and noticed, that I actually didn't have this and this was the error.

Triggering writes with Boost::asio

I have some software that I would like to make a TCP client. I don't know if this is the best architecture, but in my software I spawn a thread that will be used for the Network I/O. If there is a better architecture, I'd appreciate some pointers and advice.
Both threads have a refernce to the boost::asio::io_service object and a Session object that encapsulates the socket object. The sesson object is roughly as follows:
class Session
{
public:
Session(
boost::asio::io_service & io_service,
std::string const & ip_address,
std::string const & port)
: io_service_(io_service),
resolver_(io_service),
socket_(io_service),
ip_address_(ip_address),
port_(port),
{}
virtual void start();
virtual ~Session();
virtual void stop();
void write(std::string const & msg);
void handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_close();
void handle_write(const boost::system::error_code & error);
private:
boost::asio::io_service & io_service_;
boost::asio::ip::tcp::resolver resolver_;
boost::asio::ip::tcp::socket socket_;
std::string ip_address_;
std::string port_;
};
In the I/O thread run-loop, the start() method of the session object is called which connects to the server. (This works, btw). Then, the thread sits in a loop calling the run() method on the I/O service object [io_service_.run()] to trigger events.
The main thread calls the write() method of the session when it wants to send data, and the session object calls boost::async_write with the data to write and then a callback method that is a member of the session object (handle_write).
While I have the I/O thread connecting to the server, I cannot get the handle_write method to be triggered. I have verified that the main thread is calling into the session object and executing async_write() on the socket. It is just that the callback is never triggered. I also don't see any data on the server side or over the wire with tcpdump.
Any idea where my problem might be? Is there a better way to organize the architecture? Most of all, I don't want to block the main thread doing I/O.
Here is the code that spawns the io thread from the main thread (apologies for the spacing):
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
The code for the start() method is as follows:
void Session::start()
{
typedef boost::asio::ip::tcp tcp;
tcp::resolver::query query(
tcp::v4(),
ip_address_,
port_);
resolver_.async_resolve(
query,
boost::bind(
&Session::handle_resolve,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
while(1){ // improve this later
io_service_.run();
}
}
The callback for the resolver:
void Session::handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
if (!error)
{
boost::asio::ip::tcp::endpoint endpoint = *endpoint_itr;
socket_.async_connect(
endpoint,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to resolve\n";
std::cerr << "Error: " << error.message() << std::endl;
}
}
The callback for connect:
void Session::handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
typedef boost::asio::ip::tcp tcp;
if (!error)
{
std::cerr << "Connected to the server!\n";
}
else if (endpoint_itr != tcp::resolver::iterator())
{
socket_.close();
socket_.async_connect(
*endpoint_itr,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to connect\n";
}
}
The write() method that the main thread can call to send post an asychronous write.
void Session::write(
std::string const & msg)
{
std::cout << "Write: " << msg << std::endl;
boost::asio::async_write(
socket_,
boost::asio::buffer(
msg.c_str(),
msg.length()),
boost::bind(
&Session::handle_write,
this,
boost::asio::placeholders::error));
}
And finally, the write completion callback:
void Session::handle_write(
const boost::system::error_code & error)
{
if (error)
{
std::cout << "Write complete with errors !!!\n";
}
else
{
std::cout << "Write complete with no errors\n";
}
}
Looks like your io service will run out of work after connect, after which you just call io_service::run again? It looks like run is being called in the while loop, however I can't see a call to reset anywhere. You need to call io::service::reset before you call run on the same io_service again.
Structurally, it would be better to add work to the io_service, then you don't need to call it in the loop and the run will exit once you call io_service::stop.
this portion of your code
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
is a red flag to me. Your io_service object is possibly going out of scope and causing strange behavior. An io_service is not copyable, so passing it to your Session as a non-const reference is probably not what you are hoping to achieve.
samm#macmini ~> grep -C 2 noncopyable /usr/include/boost/asio/io_service.hpp
#include <boost/asio/detail/epoll_reactor_fwd.hpp>
#include <boost/asio/detail/kqueue_reactor_fwd.hpp>
#include <boost/asio/detail/noncopyable.hpp>
#include <boost/asio/detail/select_reactor_fwd.hpp>
#include <boost/asio/detail/service_registry_fwd.hpp>
--
*/
class io_service
: private noncopyable
{
private:
--
/// Class used to uniquely identify a service.
class io_service::id
: private noncopyable
{
public:
--
/// Base class for all io_service services.
class io_service::service
: private noncopyable
{
public:
If you're basing your code off the HTTP client example, you should note the io_service is in scope all the time inside of main(). As Ralf pointed out, your io_service is also likely running out of work to do after the connect handler, which is why you've kludged it to invoke run() inside of a loop
while(1){ // improve this later
io_service_.run();
}
again, note that the HTTP client example does not do this. You need to start another async operation inside of the connect handler, either a read or write depending on what your application needs.

Using SSL sockets and non-SSL sockets simultaneously in Boost.Asio?

I'm in the process of converting a library to Boost.Asio (which has worked very well so far), but I've hit something of a stumbling block with regards to a design decision.
Boost.Asio provides support for SSL, but a boost::asio::ssl::stream<boost::asio::ip::tcp::socket> type must be used for the socket. My library has the option of connecting to SSL servers or connecting normally, so I've made a class with two sockets like this:
class client : public boost::enable_shared_from_this<client>
{
public:
client(boost::asio::io_service & io_service, boost::asio::ssl::context & context) : socket_(io_service), secureSocket_(io_service, context) {}
private:
boost::asio::ip::tcp::socket socket_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
};
And within there are a bunch of handlers that reference socket_. (For example, I have socket_.is_open() in several places, which would need to become secureSocket_.lowest_layer().is_open() for the other socket.)
Can anyone suggest the best way to go about this? I'd rather not create a separate class just for this purpose, because that would mean duplicating a lot of code.
Edit: I rephrased my original question because I misunderstood the purpose of an OpenSSL function.
I'm rather late in answering this question, but I hope this will help others. Sam's answer contains the germ of an idea, but doesn't quit go far enough in my opinion.
The idea came about from the observation that asio wraps an SSL socket in a stream. All this solution does is that it wraps the non-SSL socket similarly.
The desired result of having a uniform external interface between SSL and non-SSL sockets is done with three classes. One, the base, effectively defines the interface:
class Socket {
public:
virtual boost::asio::ip::tcp::socket &getSocketForAsio() = 0;
static Socket* create(boost::asio::io_service& iIoService, boost::asio::ssl::context *ipSslContext) {
// Obviously this has to be in a separate source file since it makes reference to subclasses
if (ipSslContext == nullptr) {
return new NonSslSocket(iIoService);
}
return new SslSocket(iIoService, *ipSslContext);
}
size_t _read(void *ipData, size_t iLength) {
return boost::asio::read(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
}
size_t _write(const void *ipData, size_t iLength) {
return boost::asio::write(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
}
};
Two sub-classes wrap SSL and non-SSL sockets.
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket_t;
class SslSocket: public Socket, private SslSocket_t {
public:
SslSocket(boost::asio::io_service& iIoService, boost::asio::ssl::context &iSslContext) :
SslSocket_t(iIoService, iSslContext) {
}
private:
boost::asio::ip::tcp::socket &getSocketForAsio() {
return next_layer();
}
};
and
class NonSslSocket: public Socket, private Socket_t {
public:
NonSslSocket(boost::asio::io_service& iIoService) :
Socket_t(iIoService) {
}
private:
boost::asio::ip::tcp::socket &getSocketForAsio() {
return next_layer();
}
};
Every time you call an asio function use getSocketForAsio(), rather than pass a reference to the Socket object. For example:
boost::asio::async_read(pSocket->getSocketForAsio(),
boost::asio::buffer(&buffer, sizeof(buffer)),
boost::bind(&Connection::handleRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
Notice that the Socket is stored as pointer. I cannot think how else the polymorphism can be hidden.
The penalty (which I don't think great) is the extra level of indirection used to obtain non-SSL sockets.
There's a couple of ways you can do this. In the past, I've done something like
if ( sslEnabled )
boost::asio::async_write( secureSocket_ );
} else {
boost::asio::async_write( secureSocket_.lowest_layer() );
}
Which can get messy pretty quickly with a lot of if/else statements. You could also create an abstract class (pseudo code - oversimplified)
class Socket
{
public:
virtual void connect( ... );
virtual void accept( ... );
virtual void async_write( ... );
virtual void async_read( ... );
private:
boost::asio::ip::tcp::socket socket_;
};
Then create a derived class SecureSocket to operate on a secureSocket_ instead of socket_. I don't think it would be duplicating a lot of code, and it's probably cleaner than if/else whenever you need to async_read or async_write.
The problem of course is that tcp::socket and the ssl "socket" don't share the any common ancestor. But most functions for using the socket once it's open share the exact same syntax. The cleanest solution is thus with templates.
template <typename SocketType>
void doStuffWithOpenSocket(SocketType socket) {
boost::asio::write(socket, ...);
boost::asio::read(socket, ...);
boost::asio::read_until(socket, ...);
// etc...
}
This function will work work with normal tcp::sockets and also secure SSL sockets:
boost::asio::ip::tcp::socket socket_;
// socket_ opened normally ...
doStuffWithOpenSocket<boost::asio::ip::tcp::socket>(socket_); // works!
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
// secureSocket_ opened normally (including handshake) ...
doStuffWithOpenSocket(secureSocket_); // also works, with (different) implicit instantiation!
// shutdown the ssl socket when done ...
It would compile with something like this:
typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> Socket_t;