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.
Related
I have a class that looks like this:
class MyConnector : public boost::noncopyable, public boost::enable_shared_from_this<MyConnector>
{
public:
typedef MyConnector this_type;
boost::asio::ip::tcp::socket _plainSocket;
boost::shared_ptr<std::vector<uint8_t>> _readBuffer;
// lot of obvious stuff removed....
void readProtocol()
{
_readBuffer = boost::make_shared<std::vector<uint8_t>>(12, 0);
boost::asio::async_read(_plainSocket, boost::asio::buffer(&_readBuffer->at(0), 12),
boost::bind(&this_type::handleReadProtocol, shared_from_this(),
boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error));
}
void handleReadProtocol(size_t bytesRead,const boost::system::error_code& error)
{
// handling code removed
}
};
This class instance is generally waiting to receive 12 bytes protocol, before trying to read the full message. However, when I try to cancel this read operation and destroy the object, it doesn't happen. When I call _plainSocket.cancel(ec), it doesn't call handleReadProtocol with that ec. Socket disconnects, but the handler is not called.
boost::system::error_code ec;
_plainSocket.cancel(ec);
And the shared_ptr of MyConnector object that was passed using shared_from_this() is not released. The object remains like a zombie in the heap memory. How do I cancel the async_read() in such a way that the MyConnector object reference count is decremented, allowing the object to destroy itself?
Two things: one, in handleReadProtocol, make sure that, if there is an error, that readProtocol is not called. Canceled operations still call the handler, but with an error code set.
Second, asio recommends shutting down and closing the socket if you're finished with the connection. For example:
asio::post([this] {
if (_plainSocket.is_open()) {
asio::error_code ec;
/* For portable behaviour with respect to graceful closure of a connected socket, call
* shutdown() before closing the socket. */
_plainSocket.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
if (ec) {
Log(fmt::format("Socket shutdown error {}.", ec.message()));
ec.clear();
}
_plainSocket.close(ec);
if (ec)
Log(fmt::format("Socket close error {}.", ec.message()));
}
});
I develop a desktop chat with boost asio and beast (for browser support).
I use this architecture :
But, when building, I have an issue : bad_weak_ptr, I don't know what is wrong :s
Here a link to the source
https://onlinegdb.com/BkFhDGHe4
Update1 :
I remove run() function into constructor and move it into handle_accept function, tcp_server class. like this:
void tcp_server::handle_accept(const boost::system::error_code ec, websocket_session_ptr new_websocket)
{
if (!ec)
{
// Happens when the timer closes the socket
if(ec == boost::asio::error::operation_aborted)
return;
new_websocket->run(); //Here
chatwebsocketsessionpointer session = chat_websocket_session::create(room, new_websocket);
room->join(session);
wait_for_connection();
}
}
I can see the chat_webocket_session is deleted, but still have issue with bad_weak_ptr
Update 2 :
I found where is the issue.
If I never call do_read() function, there is no error, and I can connect to server with ws
If I call it into wait_for_data from chat_websoket_session class, I have issue.
So I must found how call do_read()
Update 3 :
If I do
websocket_session_ptr new_websocket(new websocket_session(std::move(socket)));
acceptor.async_accept(
socket,
boost::bind(
&tcp_server::websocket_accept,
this,
boost::asio::placeholders::error,
new_websocket
));
making ref to : boost beast websocket example, I accept first the socket, and after I accept the websocket with m_ws.async_accept() but I have now Bad file descriptor which means the socket is not open.
P.S: I update the ide URL (GDB online debugger)
You're using the shared pointer to this from inside the constructor:
websocket_session::websocket_session(tcp::socket socket)
: m_ws(std::move(socket))
, strand(socket.get_executor())
{
run();
}
Inside run() you do
void websocket_session::run() {
// Accept the websocket handshake
std::cout << "Accepted connection" << std::endl;
m_ws.async_accept(boost::asio::bind_executor(
strand, std::bind(&websocket_session::on_accept, , std::placeholders::_1)));
}
That uses shared_from_this() which will try to lock the unitialized weak_ptr from enable_shared_from_this. As you can see in the documentation that throws the std::bad_weak_ptr exception (ad. 11)
The documentation to shared_from_this explicitly warns against this:
It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr (in particular, shared_from_this cannot be called in a constructor).
I have one problem.
I'm developing chat server, using boost::asio.
and Here,
void CServerSocket::StartAccept(boost::asio::ip::tcp::acceptor &acceptor)
{
std::shared_ptr<boost::asio::ip::tcp::socket> socket(new boost::asio::ip::tcp::socket(acceptor.get_io_service()));
acceptor.async_accept(*socket, std::bind(&CServerSocket::OnAccept, boost::asio::placeholders::error, socket,
std::ref(acceptor)));
}
void CServerSocket::OnAccept(const boost::system::error_code &error, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
boost::asio::ip::tcp::acceptor &acceptor)
{
if (error)
{
CLogManager::WriteLog((boost::format("Accept error! : %1%") % error.message()).str().c_str());
return;
}
m_SocketList.push_back(std::make_shared<CConnectionSocket>(this, socket));
StartAccept(acceptor);
}
At std::bind, there are an error occurred.
"Error c2064 term does not evaluate to a function taking 3 arguments"
What should i do?
thanks.
If you're using std::bind, replace boost::asio::placeholders::error with std::placeholders::_1.
An accept handler may only take an error code as a parameter, see: AcceptHandler.
I recommend making acceptor a member of CServerSocket then changing the call to async_accept
to:
acceptor.async_accept(*socket, std::bind(&CServerSocket::OnAccept, this,
std::placeholders::_1));
and accessing acceptor within the OnAccept member function.
I have an existing c++ code.
boost::asio::ip::address m_sender_IP_address;
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
boost::bind(&Udp_comm::handle_receive, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Udp_comm::handle_receive(const boost::system::error_code& error, const std::size_t bytes_transferred)
{
std::string recvd_message(m_recv_buffer.begin(), m_recv_buffer.begin() + bytes_transferred);
m_sender_IP_address = m_sender_endpoint.address();//////////wait here
//continue to listening to future messages
start_receive();
process_message(m_sender_IP_addres.to_string(), recvd_message);
}
How to protect m_sender_IP_address from getting overwritten by next UDP message received? I want lock access to that variable from 2nd line in handle_receive method to the 1st line in process_message method
can I also acheive something similar without using locks?
A semaphore is not necessary here. You can use a strand to ensure an asynchronous handler is invoked concurrently once at most. To do this, create a wrapped handler for the async_receieve_from() callback
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
m_strand.wrap(
boost::bind(
&Udp_comm::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
)
);
}
Here m_strand is a member of Udp_comm. Doing this will guarantee only one thread can mutate m_sender_IP_address.
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");
}