I am trying the following:
boost::shared_ptr< tcp::socket > socket(new tcp::socket( *io_service));
boost::bind(&function, *socket); // compiler error: noncopyable error
function(*socket); // this works fine
void
function(tcp::socket & socket)
{
//do work
}
Why do I get an error there using boost::bind?
I later tried the following:
boost::shared_ptr< tcp::socket > socket(new tcp::socket( *io_service));
boost::bind(&function, socket); //this works fine now
void function(boost::shared_ptr< tcp::socket > socket)
{
asio::read_until(&socket, buffer, "end"); // compiler error: says i am passing an invalid argument
}
Why doesn't this work now?
I know I am lacking basic knowledge of C/C++ programming.
If anyone could include a link that helps with issues like this, it would be great.
You're trying to copy a noncopyable object. Wrap it in a boost::ref object to hold it as a reference:
boost::bind(&function, boost::ref(*socket));
tcp::socket is non-copyable, you need to pass it as a reference:
boost::bind(&function, boost::ref(*socket));
You should probably stick to your second version as you will not have to worry about life-time of the socket object.
boost::bind(&function, socket);
void function(boost::shared_ptr< tcp::socket > socket)
{
asio::read_until(*socket, buffer, "end");
}
Related
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.
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.
The code below successfully sends an async message to the given endpoint.
// message is a boost::shared_ptr<std::string>
// open a UDP socket
boost::asio::ip::udp::socket socket(ioService);
socket.open(boost::asio::ip::udp::v4());
// create the remote endpoint
boost::asio::ip::udp::endpoint remoteEndpoint(boost::asio::ip::address_v4::from_string(address), port);
// asynchronously send a datagram to the remote endpoint
socket.async_send_to(boost::asio::buffer(*message),
remoteEndpoint,
boost::bind(&MyClass::handler,
this,
message,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
socket.close();
However, if I change the type of message to a std::shared_ptr<std::string> rather than a boost::shared_ptr<std::string> then the call to async_send_to doesn't compile.
The error is:
boost/boost/bind/bind.hpp:457:9: No matching function for call to object of type 'boost::_mfi::mf3<void, MyClass, const boost::shared_ptr<std::__1::basic_string<char> > &, const boost::system::error_code &, unsigned long>'.
Can someone explain what is wrong? Is it possibly because I'm using boost::bind?
Looks like problem is, that you handler function receives boost::shared_ptr, not std::shared_ptr and boost::shared_ptr is not constructible from std::shared_ptr.
I am trying to make a class that manipulates with Boost sockets to make the conections simple to use.
My SocketClient class has a few properties with boost::asio::ip::tcp::socket being one of them. But I get C2512 error in my constructor, because boost::asio::ip::tcp::socket cannot exist unitialised, as it has no constructor.
Here, see the code of the class:
class SocketClient {
private:
int port; //Port, currently unused
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver::iterator endpoint_iterator;
boost::asio::ip::tcp::socket sock; //This causes the error
//It wants to be like this (impossible too):
//boost::asio::ip::tcp::socket sock(io_service);
public:
void init(const char*, const char* );
SocketClient(); //Default constructor
bool connect();
bool read(int bytes, char *text);
bool send(int length, char *text);
bool send(std::string text);
unsigned int timeout;
};
And here is the constructor:
SocketClient::SocketClient() { //ERROR: (23): error C2512: 'boost::asio::basic_stream_socket<Protocol>' : no appropriate default constructor available
sock=boost::asio::ip::tcp::socket(io_service); //Adding this didn't help
}
So what to do? Do I have to keep sock as void*?
Use initialization lists:
SocketClient::SocketClient()
:
sock(io_service)
{
// Other initialization code here...
}
Be careful though: this is well-defined because member variables are constructed in the order they appear in the class definition, and io_service appears before sock. If that wasn't the case, you would pass an uninitialized object to the constructor of socket, most likely resulting in Undefined Behavior.
If you are using C++ 11, you can do it like this:
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver::iterator endpoint_iterator;
boost::asio::ip::tcp::socket sock { io_service };
But (as Andy Prowl said) io_service must be must be placed before sock in the member list.
This would compile, but may lead to unpredictable errors:
boost::asio::ip::tcp::socket sock { io_service };
boost::asio::io_service io_service;
generally - as already stated - whenever possible use initialization lists. also use a naming scheme that identifies members.
also already stated - initialization depends on member order. if you have dependencies between members this is most often a sign for a design error. it smells. at least document this well in the code - there is always a next maintainer. of course you can alway hold elements via typed (smart) pointers.
for the concrete problem I'd suggest to pass the io_service via reference. this would give you more control over the io_service (eg. use async mode and run multiple sockets within one io_service)
#include <boost/asio.hpp>
class SocketClient {
private:
boost::asio::io_service& m_io_service;
boost::asio::ip::tcp::socket m_socket;
[...]
public:
SocketClient(boost::asio::io_service& io_service);
[...]
};
SocketClient::SocketClient(boost::asio::io_service& io_service)
: m_io_service(io_service)
, m_socket(io_service)
[...]
{
}
header .h
boost::asio::ip::tcp::socket *sock;
body .cpp
sock = new boost::asio::ip::tcp::socket(service);
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;