boost.asio echo client crash

According to the async_tcp_echo_server.cpp from here http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/examples/cpp03_examples.html

I made a client to test this. but after all connections established for a while. I closed the server.then the client would output the received message for a while. then the client crashed in handle_read. I really don't know where is wrong.

could you help me out thank you

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  #include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
using namespace std;
static int id = 1;
const char message[] = "test write string...";
class echo_session
{
	public:
		echo_session(boost::asio::io_service& io_service) 
			: socket_(io_service)
		{
			id_ = id;
			++id;
		}
		void start(const std::string& ip, const std::string& port)
		{
			tcp::resolver resolver(socket_.get_io_service());
			tcp::resolver::query query(tcp::v4(), ip, port);
			tcp::resolver::iterator iterator = resolver.resolve(query);

			socket_.async_connect(*iterator, boost::bind(&echo_session::handle_connect, this, boost::asio::placeholders::error));
		}
	private:
		void handle_connect(const boost::system::error_code& error)
		{
			if (!error)
			{
				//send
				boost::asio::async_write(socket_, 
						boost::asio::buffer(message, sizeof(message)),
						boost::bind(&echo_session::handle_write, this,
							boost::asio::placeholders::error));


				//ready to read
				boost::asio::async_read(socket_, boost::asio::buffer(buf_, sizeof(buf_)),
					boost::bind(&echo_session::handle_read, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
			}
			else
			{
				cout << "[CONNECT] " << error << endl;
				delete this;
			}
		}
		void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
		{
			if (!error)
			{
				cout << id_ << ":receive:" << bytes_transferred << "," << buf_ << endl;
				
				//cycle
				handle_connect(error);
			}
			else
			{
				cout << "[READ] " << error << endl;
				delete this;
			}
		}

		void handle_write(const boost::system::error_code& error)
		{
			if (!error)
			{
				//do nothing
			}
			else
			{
				cout << "[WRITE] " << error << endl;
				delete this;
			}
		}
		int id_;
		tcp::socket socket_;
		char buf_[sizeof(message)];
};
int main(int argc, char* argv[])
{
	const int session_num = 10000;		
	echo_session* sessions[session_num];
	memset(sessions, 0, sizeof(sessions));

	try
	{
		boost::asio::io_service io_service;

		for (int i=0; i<session_num; ++i)
		{
			sessions[i] = new echo_session(io_service);
			sessions[i]->start("127.0.0.1", "8008");
		}

		io_service.run();
	}
	catch (std::exception& e)
	{
		std::cerr << "Exception: " << e.what() << "/n";
	}

	cout<<"stoppped"<<endl;
	getchar();

	return 0;
}


I think I found the problem

in handle_connect I have two async_** operation
if the frist one cause error, would invoke handle_close and delete this
then the second async_*** would be excute
at this moment,since delete this called, hooohooo

but,still

It seems that,there must have some sequnce,
first connect ,then in handle_connect invoke an async_write
then in handle_write invoke an async_read
then in handle_read invoke an async_write

just like this:
1 OnConnect -->SendSome
2 OnSend -->RecvSome
3 OnRecv -->SendSome

if I wangt to send two messages in OnRecv,this would cause the former problem
Is this right? also,after connection has established, I can only send something in
handle_read or handle_write, this is too passive.
I want to send something to the endpoint any time.
How could I do so using boost.asio?

thank you



Last edited on
at this moment,since delete this called, hooohooo

indeed, delete this; is very hard to use correctly. The easiest thing if you want to use this style of using asio is to follow boost examples and make a shared_from_this to handle your sessions.
Same example, updated to C++11, in fact: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp

I want to send something to the endpoint any time. How could I do so using boost.asio?

Look at client examples, rather than servers. For example, http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp11/chat/chat_client.cpp
than you Cubbi

I tried the two examples you suggest

I found the problem point is bellow

If I connected to a server,and keep sending messages to the server, for example 10000 messages by 10000 times async_write calls
In the middle of this peocess,the server was killed,meanwhile,suppose there were 100 async_write in io_service waiting to be excuted.
One of them would cause error and invoke handle_write with this error,of course I want to do something at this moment,close the socket,remove the session from session manager,or maybe invoke a m_pClient->OnClose callback.
but but but after this has been done,another write would be excuted by io_service, as before,I would do the same thing,now crash!!! this time m_pClient is wild which I am sure it is not been deleted. I tried shared_ptr,the same happens.it's because the error handling has been done before and can't be done again
I hope I have described my problem correctly.thank you
opengl wrote:
of course I want to do something at this moment

It's not self-evident, but you are on the right track

opengl wrote:
close the socket

yes, that's a reasonable thing to do and all simple examples do that (and nothing else)

socket_.close(); in do_write from http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp11/chat/chat_client.cpp

close(); in handle_write_output from http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/chat/posix_chat_client.cpp

do_close() in handle_write of http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/chat/chat_client.cpp

If you look at the documentation http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_socket/close/overload1.html , this has the following effect:

boost wrote:
Any asynchronous send, receive or connect operations will be cancelled immediately, and will complete with the boost::asio::error::operation_aborted error.


opengl wrote:
remove the session from session manager

Session here is the object that controls the lifetime of the socket, it can't be destroyed until the last outstanding request that uses that socket completes (note that they will complete very quickly if the socket is shut down or closed!)

That's what makes shared_from_this a fit for this use case. Note how this exact thing is done at http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp11/http/server/connection.cpp : first socket_.shutdown(both), then, if the error code is not operation_aborted (remember, you get that when your socket was closed by another handler on the same connection) it calls connection_manager_.stop(shared_from_this()); which removes the shared_ptr from the manager's container and calls its socket_.close();
Last edited on
thank you Cubbi

first I confirmed that if any error occur I would call handle_close int handle_****,
and in connection_manager_.stop,would call session.close(),
which only close the socket:socket_.close()

1
2
3
4
5
6
7
void session::handle_close(const boost::system::error_code& error,int close_type)
{
	if (error != boost::asio::error::operation_aborted)
	{
		connection_manager_.stop(shared_from_this());
	}
}


but I debuged the code step by step, I found that

connection_manager_.stop(shared_from_this());

was actually been invoked twice,one from handle_read,one from handle_write (I only establish one connection to the server)

so, the frist call was correct,but the second would go failure

since what you have said, any asynchronous send, receive or connect operations will be cancelled immediately,so after connection_manager_.stop(shared_from_this()) was called which close the socket,how could it been invoked again???
hi Cubbi
I think I found the tricky

1
2
3
4
boost::asio::async_read(socket_,
			boost::asio::buffer(read_msg_.data(), message::header_length),
			boost::bind(&session::handle_read_header, shared_from_this(),
			boost::asio::placeholders::error));


I changed the second parameter in boost::bind from this into shared_from_this()
now every thing is right
handle_close would be invoked twice,one from handle_read,one from handle_write
Topic archived. No new replies allowed.