The code snippet below always close the connection at once when a client has connected to the server, which is really out of my expection.
#include <iostream>
#include <memory>
#include <system_error>
#include "tcp_server.hpp"
TcpServer::TcpServer(asio::io_context& io_context,
const unsigned int& port):m_acceptor(io_context, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
void TcpServer::do_accept()
{
m_acceptor.async_accept(make_strand(m_acceptor.get_executor()),
[this](const std::error_code& error, tcp::socket socket)
{
if (!error)
{
std::make_shared<Conversation>(std::move(socket))->do_start();
}
else
{
std::cerr << "Error accepting connection: " << error.message() << std::endl;
}
// Start accepting the next connection
do_accept();
});
}
Conversation::Conversation(tcp::socket socket) : m_socket(std::move(socket))
{
std::cout << "creating conversation:" << this << std::endl;
}
void Conversation::do_read()
{
std::cout << "do read" << std::endl;
auto conversation_ptr{shared_from_this()};
m_socket.async_read_some(asio::buffer(m_data),
[this, conversation_ptr](std::error_code error, size_t length) {
if (!error)
{
do_write(std::string("reply:") + std::string{m_data.data(), length});
do_read();
std::cout << "data read: " << m_data.data() << std::endl;
}
else
{
std::cout << "error in reading data: " << error.message() << std::endl;
}
});
}
void Conversation::do_start()
{
do_read();
}
void Conversation::do_write(std::string str)
{
auto conversation_ptr{shared_from_this()};
m_socket.async_send(asio::buffer(str), [this, conversation_ptr](std::error_code error, size_t length ){
if (!error)
{
std::cout << "data write successfully" << std::endl;
}
else
{
std::cout << "error in writing data: " << error.message() << std::endl;
}
});
}
Conversation::~Conversation()
{
std::cout << "destroying conversation:" << this << std::endl;
}
int main()
{
asio::io_context io_context;
TcpServer server{io_context, 12345};
io_context.run();
}
Since auto conversation_ptr{shared_from_this()};
constructs an instance of std::initializer_list<std::shared_ptr<Conversation>>
and the complete handler captures the conversation_ptr
, I think the instance of Conversation
would be kept alive when the complete hander is invoked if some message is sent from the client.However the output of the server always something like this below.
./tcp_server
creating conversation:0x1cad8d0
do read
destroying conversation:0x1cad8d0
error in reading data: Operation aborted.
^C
At the mean time, here is the output of telnet
below, which idicates the server other than the client closes the connection.
telnet 0.0.0.0 12345
Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.
Connection closed by foreign host.
What’s more, it works well if the auto conversation_ptr{shared_from_this()};
is replaced by auto conversation_ptr(shared_from_this());
. What a surprise!
The used compiler is clang++ 3.4.
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
Target: x86_64-pc-linux-gnu
Thread model: posix
The used asio library is V1.29.0.
#define ASIO_VERSION 102900 // 1.29.0
The platform is ubuntu14.04.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.6 LTS
Release: 14.04
Codename: trusty
NOTE: As per this post, the definition of max_align_t
has been added to make the code snippet compile with clang++ 3.4.
2
Answers
Thanks to Artyer.
For the readers who read this post later, how the different versions of compilers work with the same code snippet would help to fully understand it.
std::initializer_list
holds a reference to a temporary array. This is lifetime extended like a normalconst
reference, i.e., the temporary ends its lifetime when the originalinitializer_list
ends its lifetime.So, the captured copy
initializer_list
becomes dangling once the lambda is called: theshared_ptr
it was referring to has already been destroyed. The reasonauto conversation_ptr(shared_from_this());
works is that you capture ashared_ptr
directly and the lambda holds a copy.Also
auto conversation_ptr{shared_from_this()};
only makes astd::initializer_list<std::shared_ptr<Conversation>>
before N3922. Afterwards, it becomes astd::shared_ptr<Conversation>
. Starting from Clang 3.6, you started getting this warning:And was finally implemented in Clang 3.8, so I suspect this code was written for a newer compiler