Passing around boost::asio::ip::tcp::socket - c++

Alright, this is my current code snippet:
namespace bai = boost::asio::ip;
bai::tcp::socket tcp_connect(std::string hostname, std::string port) {
try {
boost::asio::io_service io_service;
bai::tcp::resolver resolver(io_service);
// we now try to get a list of endpoints to the server
bai::tcp::resolver::query query(hostname, port);
bai::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
bai::tcp::resolver::iterator end;
// looking for a successful endpoint connection
bai::tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end) {
socket.close();
socket.connect(*endpoint_iterator++ , error);
}
if (error) throw boost::system::system_error(error);
return socket;
} catch (std::exception &ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
}
Which should return a boost::asio::ip::tcp::socket connected to hostname on port. However I get presented with a shitload of incomprehensible boost::noncopyable errors. But my question is, how should I pass around these sockets then? What's wrong with this?

socket can't be copied. Use a boost::shared_ptr<bai::tcp::socket> instead. If you could copy a socket you'd have all sorts of funny issues if you ended up with two socket instances representing the same underlying OS socket - so it makes sense that copying (and therefore return by value, pass by value) is not allowed.

The code:
return socket;
attempts to make a copy of socket to return, and then destroy the original socket when the function exits. Unfortunately, sockets cannot be copied (they manage an underlying operating system resource that must be closed, so the system must ensure only one reference to that resource exists, otherwise things would go badly wrong when the first copy went out of scope).
As suggested in the other answer, you could use a pointer to return an object created on the heap (which should be managed either with a shared_ptr, or more efficiently if you are only using it in a single place a unique_ptr), or if you are using C++11 you could use the move constructor for the return value:
return std::move (socket);
This would avoid the necessity to use heap allocation and pointers, so is probably the preferable solution.

Related

Why we can reuse a moved socket_ in acceptor_.async_accept?

Reference: https://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept/overload1.html
boost::asio::ip::tcp::acceptor acceptor(io_service);
...
boost::asio::ip::tcp::socket socket(io_service);
// you have to initialize socket with io_service first before
//you can use it as a parameter on async_accept.
acceptor.async_accept(socket, accept_handler);
Reference:
https://github.com/vinniefalco/CppCon2018/blob/master/listener.cpp
listener:: listener(
net::io_context & ioc,
tcp::endpoint endpoint,
std::shared_ptr < shared_state >
const & state): acceptor_(ioc), socket_(ioc), state_(state) {
// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener>
{
tcp::acceptor acceptor_;
tcp::socket socket_;
...
}
// Handle a connection
void listener:: on_accept(error_code ec) {
if (ec)
return fail(ec, "accept");
else
// Launch a new session for this connection
std::make_shared < http_session > (
std::move(socket_), // socket_ is moved here?
state_) -> run();
// Accept another connection
acceptor_.async_accept(
socket_, // why we still can use it here?
[self = shared_from_this()](error_code ec) {
self -> on_accept(ec);
});
}
Based on my understanding, std::move(socket_) allows the compiler to cannibalize socket_. In other word, the listener::socket_ originally initialized by socket_(ioc) will become uninitialized.
Question> How can we give an uninitialized socket_ to acceptor_.async_accept?
Thank you
It all depends on the implementation of the types.
We can loosely describe the intent of a move as "the compiler is allowed to cannibalize". But really, for user-defined types we're going to have to tell it how to do that, exactly.
In language "doctrine" a moved-from object may only be assumed safe to destruct, but in practice many libraries make more lenient guarantees (e.g. keeping all the invariants, or making sure that a moved-from object is comparable to a newly constructed one).
Indeed, ASIO documents this:
Remarks
Following the move, the moved-from object is in the same state as if constructed using the basic_stream_socket(const executor_type&) constructor.

how to determine whether boost async_accept is listening on a port or not

I am unable to get this example working
http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html
I have changed the port 13 to 1163 so that I don't need to be a root user to start listening.
And I am running the io_service in separate thread.
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.detach();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
string wait;
cin >> wait;
return 0;
}
When testing the above server with http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/tutorial/tutdaytime1/src.html client, it says connection refused.
netstat --listen didn't show any open ports on 1163
I couldn't figure out how to use boost::asio::async_result<typename Handler> I am confused on Handler.
Working modification
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.detach();
string wait;
cin >> wait;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
If the wait is inside the try block, the code working!
If asio can't listen on the port the creation or binding of the acceptor will already fail. In your case you are creating the acceptor with acceptor_(io_service, tcp::endpoint(tcp::v4(), 13)), which is this overload: http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/basic_socket_acceptor/basic_socket_acceptor/overload3.html
This will directly try to bind the socket and throw an exception if that fails. The alternative (which is described on the bottom of this page) is to create the socket without an assigned endpoint and call open and bind on it, where bind will fail with an error (e.g. if the socket is already in use). In any case you won't need to wait for accept/async_accept to see an error.
I guess your issue is in the client program, which still tries to connect to port 13. Have you changed it to use port 1163? In the example code the port is not directly written, but a well-known service name is used here: tcp::resolver::query query(argv[1], "daytime");. The "daytime" reference will tell the resolver to use port 13.
Update: Now that that I see the actual code with the thread it's a totally different error:
If the wait is not inside the try block the asio io_service and tcp_server will go out of scope nearly immediatly, which means their destructors are called. This will stop all communication. Even worse, the detached thread now operates on some dangling pointers. As a general rule asio objects (the io_service eventloop, sockets, etc.) should all live in the the thread that uses them. The lifetime of the io_service should be tied to or shorter than the lifetime of a thread. The lifetime of the sockets should be shorter than that of the eventloop (io_service) that runs them. Other usage scenarios might be possible by using shared_ptr's or by putting a lots of thoughts into the design, but that's not what I would recommend.

Handling socket boost asio tcp ip - C++ socket programming

I am successfully establishing connection, sending and receiving messages using the following code. What I want to do is somehow to return that already established connection. I assume that I need to return the socket.
Before writing this topic I read a few related topics - in some of them it was mentioned that returning a socket is not a good idea. Usage of shared is suggested here. Passing around boost::asio::ip::tcp::socket
Unfortunately I am not familiar with this type of pointers and their usage. Could you help me with fixing that problem.
try {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(server, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
my_socket = new tcp::socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::connect(*my_socket, endpoint_iterator);
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
if you have c++11 or higher, ignore all that nonsense about shared pointers and return the socket. As of c++11 asio io objects support move construction and move assignment. An asio socket is extremely lighweight - the structure contains two pointers, nothing more. – Richard Hodges

Persistent ASIO connections

I am working on a project where I need to be able to use a few persistent to talk to different servers over long periods of time. This server will have a fairly high throughput. I am having trouble figuring out a way to setup the persistent connections correctly. The best way I could think of to do this is create a persistent connection class. Ideally I would connect to the server one time, and do async_writes as information comes into me. And read information as it comes back to me. I don't think I am structuring my class correctly though.
Here is what I have built right now:
persistent_connection::persistent_connection(std::string ip, std::string port):
io_service_(), socket_(io_service_), strand_(io_service_), is_setup_(false), outbox_()
{
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(ip,port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *iterator;
socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator));
io_service_.poll();
}
void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
if(ec)
{
std::cout << "Couldn't connect" << ec << std::endl;
return;
}
else
{
boost::asio::socket_base::keep_alive option(true);
socket_.set_option(option);
boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n", boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}
}
void persistent_connection::write(const std::string &message)
{
write_impl(message);
//strand_.post(boost::bind(&persistent_connection::write_impl, this, message));
}
void persistent_connection::write_impl(const std::string &message)
{
outbox_.push_back(message);
if(outbox_.size() > 1)
{
return;
}
this->write_to_socket();
}
void persistent_connection::write_to_socket()
{
std::string message = "GET /"+ outbox_[0] +" HTTP/1.0\r\n";
message += "Host: 10.1.10.120\r\n";
message += "Accept: */*\r\n";
boost::asio::async_write(socket_, boost::asio::buffer(message.c_str(), message.size()), strand_.wrap(
boost::bind(&persistent_connection::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
}
void persistent_connection::handle_write(const boost::system::error_code& ec, std::size_t bytes_transfered)
{
outbox_.pop_front();
if(ec)
{
std::cout << "Send error" << boost::system::system_error(ec).what() << std::endl;
}
if(!outbox_.empty())
{
this->write_to_socket();
}
boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n",boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}
The first message I will send from this seems to send out fine, the server gets it, and responds with a valid response. I see two problem unfortunately:
1) My handle_write is never called after doing the async_write command, I have no clue why.
2) The program never reads the response, I am guessing this is related to #1, since asyn_read_until is not called until that function happens.
3) I was also wondering if someone could tell me why my commented out strand_.post call would not work.
I am guessing most of this has to due with my lack of knowledge of how I should be using my io_service, so if somebody could give me any pointer that would be greatly appreciated. And if you need any additional information, I would be glad to provide some more.
Thank you
Edit call to write:
int main()
{
persistent_connection p("10.1.10.220", "80");
p.write("100");
p.write("200");
barrier b(1,30000); //Timed mutex, waits for 300 seconds.
b.wait();
}
and
void persistent_connection::handle_read_headers(const boost::system::error_code &ec)
{
std::istream is(&buf_);
std::string read_stuff;
std::getline(is,read_stuff);
std::cout << read_stuff << std::endl;
}
The behavior described is the result of the io_service_'s event loop no longer being processed.
The constructor invokes io_service::poll() which will run handlers that are ready to run and will not block waiting for work to finish, where as io_service::run() will block until all work has finished. Thus, when polling, if the other side of the connection has not written any data, then no handlers may be ready to run, and execution will return from poll().
With regards to threading, if each connection will have its own thread, and the communication is a half-duplex protocol, such as HTTP, then the application code may be simpler if it is written synchronously. On the other hand, if it each connection will have its own thread, but the code is written asynchronously, then consider handling exceptions being thrown from within the event loop. It may be worth reading Boost.Asio's
effect of exceptions thrown from handlers.
Also, persistent_connection::write_to_socket() introduces undefined behavior. When invoking boost::asio::async_write(), it is documented that the caller retains ownership of the buffer and must guarantee that the buffer remains valid until the handler is called. In this case, the message buffer is an automatic variable, whose lifespan may end before the persistent_connection::handle_write handler is invoked. One solution could be to change the lifespan of message to match that of persistent_connection by making it a member variable.

boost::asio error "The I/O operation has been aborted..."

I am receiving this error message
"The I/O operation has been aborted because of either a thread exit or an application request"
when using boost::asio::socket::async_read_some()
What does the error mean? What should I be looking for?
Here is the relevant code:
void tcp_connection::start()
{
printf("Connected to simulator\n");
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void tcp_connection::read_sim_handler(
const boost::system::error_code& error, // Result of operation.
std::size_t len ) // Number of bytes read.
{
try {
if (error == boost::asio::error::eof) {
// Connection closed cleanly by peer.
printf("Sim connection closed\n");
return;
} else if (error) {
throw boost::system::system_error(error); // Some other error. if( ! error )
}
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
When I replace the call to async_read_some() with read_some() in the start() method, everything works fine ( except the server blocks waiting for a message! )
Following a comment i see that tcp_connection is going out of scope. I copied the code from http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/tutorial/tutdaytime3.html
which says this:
"We will use shared_ptr and enable_shared_from_this because we want to keep the tcp_connection object alive as long as there is an operation that refers to it."
I confess that I do not know what all that means. So I have broken it somehow?
Following further comments, the answer is
void tcp_connection::start()
{
printf("Connected to simulator\n");
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Passing shared_from_this() rather than this employs the clever ( too clever? ) keep alive infrastructure established by the server code, even though the connection manager is not in scope, by normal means. For technical details, see comments under accepted answer.
Your tcp_connection object or your buffer object is likely going out of scope prior to the async operation completing.
Since your program is based on one of the tutorial examples, why don't you check out another of the examples that reads some data as well: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
The reason your class goes out of scope is that you are no longer using shared_from_this(). What this does is create a shared_ptr to your class that is stored by the bind handler. This means that the shared_ptr will keep your class alive until your handler is called.
This is also why you need to inherit from enable_shared_from_this.
The last shared_ptr that goes out of scope will delete your class instance.