Reputation: 4681
ASIO seems like the best async cross-platform networking library for my project. However, I'm having trouble getting it to actually connect.
First off, I'm not using Boost. I'm compiling this on Windows for the time being, so I had to manually add definitions to inform ASIO that I'm using a C++11-compliant compiler.
Source.cpp
#define TCPCLIENT_DEBUG
#include "TCPClient.hpp"
#include <iostream>
#define PORT "1234"
#define HOST "127.0.0.1"
int main() {
DEBUG("Starting program...\n");
namespace ip = asio::ip;
asio::io_service io;
ip::tcp::resolver::query query(HOST, PORT);
ip::tcp::resolver resolver(io);
decltype(resolver)::iterator ep_iter = resolver.resolve(query);
TCPClient client(io, ep_iter);
try {
std::cin.get();
}
catch (const std::exception &e) { // mainly to catch Ctrl+C
std::cout << e.what() << std::endl;
}
return 0;
}
TCPClient.hpp
#ifndef TCPCLIENT_HPP
#define TCPCLIENT_HPP
#include <functional>
#if defined(_DEBUG) || defined(TCPCLIENT_DEBUG)
#include <iostream>
#define DEBUG(dbg_msg) std::cerr << dbg_msg
#else
#define DEBUG(dbg_msg)
#endif
#define ASIO_STANDALONE
#define ASIO_HAS_CSTDINT
#define ASIO_HAS_STD_ARRAY
#define ASIO_HAS_STD_ADDRESSOF
#define ASIO_HAS_STD_SHARED_PTR
#define ASIO_HAS_STD_TYPE_TRAITS
#include <asio.hpp>
#ifndef BUFFER_SIZE
#define BUFFER_SIZE 1024
#endif
class TCPClient {
public:
TCPClient(asio::io_service& io, asio::ip::tcp::resolver::iterator endpoint_iter);
void on_connect(const asio::error_code& err);
private:
asio::io_service& m_io; // store the io service reference
asio::ip::tcp::socket m_sock; // object's socket
static const size_t bufSize{ BUFFER_SIZE }; // default buffer size
char m_buffer[bufSize]; // store the received data in a buffer
};
#endif//TCPCLIENT_HPP
TCPClient.cpp
#include "TCPClient.hpp"
TCPClient::TCPClient(asio::io_service& io, asio::ip::tcp::resolver::iterator endpoint_iter) : m_io{ io }, m_sock(io) {
asio::ip::tcp::endpoint endpoint = *endpoint_iter;
asio::error_code ec;
m_sock.async_connect(
endpoint,
std::bind(
&TCPClient::on_connect,
this,
std::placeholders::_1
)
);
}
void TCPClient::on_connect(const asio::error_code& err) {
DEBUG("Connected successfully!\n");
}
It seems to me that the on_connect
is never being called. It only prints "Starting program...".
Using netcat, I can spawn a listener that sees the connection successfully go through.
What is obviously wrong with my code? I'm only working on the connection function for right now.
Upvotes: 0
Views: 401
Reputation: 15567
By calling async_connect
, you only register an asynchronous operation. You should explicitly call io_service.run()
somewhere, - probably, in main
instead of std::cin.get()
, - to get your asynchronous operations really executed and callbacks called.
Under the hood, asio uses epoll
or something similar: it registers events it is interested in (a socket connection in your case) and then waits for the events to happen. io_service.run()
is precisely the place where waiting is done.
I'd advise you to look at some boost::asio asyncronous tutorials, like this one.
Upvotes: 2
Reputation: 51951
Handlers are only executed within threads that are currently running the io_service
. As the io_service
is never ran, the connect handler is never executed. To resolve this, run the io_service
by calling io_service::run()
:
TCPClient client(io, ep_iter);
try {
io.run();
}
catch (const std::exception &e) {
std::cout << e.what() << std::endl;
}
The Using a timer asynchronously Tutorial notes the importance of running the io_service
:
Finally, we must call the
io_service::run()
member function on theio_service
object.The asio library provides a guarantee that callback handlers will only be called from threads that are currently calling
io_service::run()
. Therefore unless theio_service::run()
function is called the callback for the asynchronous wait completion will never be invoked.The
io_service::run()
function will also continue to run while there is still "work" to do. In this example, the work is the asynchronous wait on the timer, so the call will not return until the timer has expired and the callback has completed.It is important to remember to give the
io_service
some work to do before callingio_service::run()
. For example, if we had omitted the above call todeadline_timer::async_wait()
, theio_service
would not have had any work to do, and consequentlyio_service::run()
would have returned immediately.
Upvotes: 2