I am trying to make a proxy that works properly only for the first session in one execution of app. It catches SIGSEGV trying to handle the second one.
It works next way:
client connects
proxy connects to end server (unique connection for each session)
proxy sends data to server, gets handled data from server and sends handled data to client
proxy breaks connection with server and client
The problem is when we start the app and the first client tries to use proxy, it works fine (let it be clients connect to proxy consistently e.g. first one got its data, disconnection occured and only then the second one connects). But when the second one tries to connect after this, execution can not even reach the handleAccept and catches SIGSEGV in __atomic_add function in atomicity.h (I am working in Linux).
I can not understand either I make handlers incorrectly, use shared_ptr's incorrectly, or both.
run is called once after creating Proxy object to make it accept and handle client connections:
void Proxy::run() // create the very first session and keep waiting for other connections
{
auto newSession = std::make_shared<Session>(ioService_);
acceptor_.async_accept(
newSession->getClientSocket(),
[&](const boost::system::error_code &error) // handler is made according to boost documentation
{
handleAccept(newSession, error);
}
);
ioService_.run();
}
handleAccept does almost the same thing but also makes session start transferring data between client and end server:
void Proxy::handleAccept(std::shared_ptr<Session> session, const boost::system::error_code &error) // handle the new connection and keep waiting other ones
{
if (!error)
{
session->connectToServer(serverEndpoint_);
session->run(); // two more shared_ptr's to session are appeared here and we just let it go (details are further)
}
auto newSession = std::make_shared<Session>(ioService_);
acceptor_.async_accept(
newSession->getClientSocket(),
[&](const boost::system::error_code &error)
{
handleAccept(newSession, error);
}
);
}
Session contains two Socket objects (server and client) each of which has shared_ptr to it. When each of them will have done all actions or some error will have occured, they reset their shared_ptr's to session so it is deallocated.
Why you use/catch local variable by reference in handleAccept(...) ?:
acceptor_.async_accept(
newSession->getClientSocket(),
[&](const boost::system::error_code &error)
{
handleAccept(newSession, error);
}
);
Would you like to use:
acceptor_.async_accept(
newSession->getClientSocket(),
[this, newSession](const boost::system::error_code &error)
{
handleAccept(newSession, error);
}
);
The lambda will be run after function will be completed, and local variable newSession will be destroied before that.
Related
I have written a threaded server much like this one: https://www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
And a client: https://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/timeouts/blocking_tcp_client.cpp
They seem to work fine together when the client is talking directly with the server session. But now I would like to create another thread that would use the servers ioservice to send small messages. How would this be possible? I have read that shared_ptr would be one option, but have not got it working...
Inside the session class I define:
typedef boost::shared_ptr<session> session1;
static session1 create(boost::asio::io_service& io_service)
{
return session1(new session(io_service));
}
Then I define a global session_ptr as
session::session1 new_session1 = nullptr;
Then in the acceptor I start the session as:
new_session1 = session::create(tcp_io_serviceServer);
acceptor_.listen();
acceptor_.async_accept(new_session1->socket(), boost::bind(&server::handle_accept, this, boost::asio::placeholders::error));
and in the handle_accept:
new_session1->start();
Now what I would like to achieve, is that when the async_read of the server session gets a message from client to start a new thread:
if (dataReceived[0] == _dataStartCameraThread)
{
pthread = boost::thread(boost::bind(StartProcess, server));
}
then in that thread I want to send messages to the client as: new_session1->write1(error) as
void write1(const boost::system::error_code& error)
{
boost::asio::async_write(tcpsocket, boost::asio::buffer(sbuf, 1), boost::bind(&session::handle_dummy, this, boost::asio::placeholders::error));
}
But without the shared_ptr approach I cannot make this work. It claims that the file handle is not valid.
And using the shared_ptr approach I cannot seem to write anything from the server side, I can only read:
write failed. The file handle supplied is not valid
I checked that the socket is closed even though it just received the message.
Any suggestions where I should go here?
Thank you!
This is my first boost::asio project and I came up with a code structure that has one server that create several sessions based on connection requests.
The sessions have a timeout mechanism where I control if I´m receiving messages from the clients. If after a period of time no messages are sent from clients, the session deletes itself and the client shall restart the connection to send its data. - this is the expected behaviour. The clients are small remote units.
This mechanism is working fine, except that I don´t understand what to do after I delete the session on timeout.
Here is the server StartAccept and HandleAccept functions of the server:
void SocketServer::StartAccept()
{
std::shared_ptr<SocketSession> session = std::make_shared<SocketSession>(ioService);
acceptor.listen();
acceptor.async_accept(session->getSessionSocket(), boost::bind(&SocketServer::HandleAccept, this, session, boost::asio::placeholders::error));
}
void SocketServer::HandleAccept(std::shared_ptr<SocketSession> session, const boost::system::error_code& errorCode)
{
if (errorCode)
{
std::cout << "Error accepting incoming connection: Error: " << boost::system::system_error(errorCode).what();
}
else
{
boost::asio::ip::tcp::socket& socket = session->getSessionSocket();
session->start();
}
StartAccept();
}
The session itself has the following code:
void SocketSession::start()
{
readTimeout.expires_from_now(boost::posix_time::seconds(10));
readTimeout.async_wait(boost::bind(&SocketSession::TimeoutHandler, shared_from_this(), boost::asio::placeholders::error));
sessionSocket.async_read_some(boost::asio::buffer(readBuffer, MAX_BUFFER_LENGTH), boost::bind(&SocketSession::HandleRead, shared_from_this(), boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error));
}
void SocketSession::HandleRead(size_t bytesTransferred, const boost::system::error_code& errorCode)
{
readTimeout.expires_from_now(boost::posix_time::seconds(10));
if (errorCode)
{
ss << "Error reading data from session: Error: " << boost::system::system_error(errorCode).what();
delete this; <<<------- PROBLEM HERE
}
else
{
std::string data(readBuffer, bytesTransferred);
std::cout << "Data read:" << data << std::endl;
start();
}
}
When the timeout is fired and the code reaches delete this, the object is destroyed (I´ve logged that in the object destructor) and the whole program goes to
double free or corruption (out): 0x0000000000e6d5c0 ***
Aborted (core dumped) (in my computer) or *** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x00000000020e0140 ***
== in Coliru.
I´m confused about what´s going on here. I´ve a full running version of the code at Coliru - link here.
What I expect is to continue running the server waiting for new connections, even if one or more session timeouts.
Help appreciated.
The way this works is that your session object is being kept alive by two shared pointers. One in the handler of the timer and one in the handler of the socket.
All you have to do is ensure that the two shared pointers go out of scope.
When your timeout function ends, one shared pointer will go out of scope (if you don't restart the timer) but the one in the socket's read handler will still exist.
So the thing to do is call cancel() on your socket. This will cause its handler to fire with the error code containing the code boost::asio::errc::operation_aborted. If you see this error in your read handler, simply exit the function.
The shared pointer will then be dropped because the handler (which holds a copy of it) will be deleted.
Once there are no shared_ptr's keeping you session alive it will be deleted and all resources will be reclaimed.
edit:
also, I noticed you used socket::read_some. This is a common mistake made by asio beginners (myself included!).
Always prefer the free function versions: asio::async_read(...) or asio::async_read_until(...). Then you don't have to take care of short reads.
ASIO is a beautiful library (IMHO) but the documentation assumes that you're an expert!
I have a server application, which uses boost.asio framework. Application logic is simple - it listens on several ports for incoming connections, accepts it, does some processing and closes connection. Of course, more than several clients allowed to connect to server at same time. I use asynchronous approach to accept connection read and write data. The problem is, that at some point of time io_service just stops to process handlers.
Let me describe symptoms in more details. After problem appears, application continues to listen to specified ports, and netstat command can verify that. Client can establish connection to server, but not a single handler(Server::Session) is called.
Here is code, that accepts connections:
void Server::StartAccept()
{
socket_ptr sock(new boost::asio::ip::tcp::socket(ioService_));
acceptor_.async_accept(*sock, boost::bind(&Server::Session, shared_from_this(), sock, boost::asio::placeholders::error));
}
void Server::Session(socket_ptr sock, const boost::system::error_code& error)
{
StartAccept();
if(error)
{
boost::system::error_code ec;
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
sock->close(ec);
return;
}
//Processing...
}
Here is the code, which starts server:
void run_service()
{
for (;;)
{
try
{
io_service.run();
break;
}
catch (...)
{
}
}
}
boost::thread_group threads;
for ( int i = 0; i < size; ++i)
threads.create_thread(run_service);
threads.join_all();
I found out out, that, if I replace line
io_service.run();
with
while (!io_service.stopped())
io_service.run_once();
than this loop will stuck right when error appears, and run_once function will never return.
My assumptions on why that could happend:
One of handler, which was called never returns.
This is some sort of deadlock in boost internals(because I don't do any locking).
The questions are:
What other reasons could be for such strange behaviour?
What is best way to fix that?
How can I figure out, which handler is called by run_once function before it stucks?
The problem was in handler, which waited for another network activity to finish. This activity didn't have timeout and in some cases lasted forever. Thanks for comments. Defining BOOST_ASIO_ENABLE_HANDLER_TRACKING is really good step to detect problem.
In the Daytime.3 tutorial for boost::asio (asynchronous TCP server), the class tcp_server contains the following two methods:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.get_io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error) new_connection->start(); // ***
start_accept();
}
My concern is the line marked ***. What if this operation takes a long time to complete? Even if it doesn't, there must be some time gap between the *** line and the call to start_accept, during which the server will fail to accept incoming connections. Wouldn't it make more sense for async_accept to register an OS handler that doesn't halt when it accepts its first connection? Also, is this a real issue and how would I fix it?
The server won't "fail to accept incoming connections"; that's what the second parameter of the listen() function is for in the sockets API. But you are correct that the server can have a delay in handling the client request. A single-threaded application that requires lots of computation will cause issues, hence why this particular example really only performs IO. If your server really does need to perform something CPU intensive, then the handler should be passed to a task manager of some sort.
I currently have a very simple boost::asio server that sends a status update upon connecting (using google proto buffers):
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service,tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
...
std::stringstream message;
protoMsg.SerializeToOstream(&message);
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message.str()), ignored_error);
}
}
catch (std::exception& e) { }
I would like to extend it to first read after accepting a new connection, check what request was received, and send different messages back depending on this message. I'd also like to keep the TCP connection open so the client doesn't have to re-connect, and would like to handle multiple clients (not many, maybe 2 or 3).
I had a look at a few examples on boost asio, namely the async time tcp server and the chat server, but both are a bit over my head tbh. I don't even understand whether I need an async server. I guess I could just do a read after acceptor.accept(socket), but I guess then I wouldn't keep on listening for further requests. And if I go into a loop I guess that would mean I could only handle one client. So I guess that means I have to go async? Is there a simpler example maybe that isn't 250 lines of code? Or do I just have to bite my way through those examples? Thanks
The examples you mention from the Boost.Asio documentation are actually pretty good to see how things work. You're right that at first it might look a bit difficult to understand, especially if you're new to these concepts. However, I would recommend that you start with the chat server example and get that built on your machine. This will allow you to closer look into things and start changing things in order to learn how it works. Let me guide you through a few things I find important to get started.
From your description what you want to do, it seems that the chat server gives you a good starting point as it already has similar pieces you need. Having the server asynchronous is what you want as you then quite easily can handle multiple clients with a single thread. Nothing too complicated from the start.
Simplified, asynchronous in this case means that your server works off a queue, taking a handler (task) and executes it. If there is nothing on the queue, it just waits for something to be put on the queue. In your case that means it could be a connect from a client, a new read of a message from a client or something like this. In order for this to work, each handler (the function handling the reaction to a particular event) needs to be set up.
Let me explain a bit using code from the chat server example.
In the server source file, you see the chat_server class which calls start_accept in the constructor. Here the accept handler gets set up.
void start_accept()
{
chat_session_ptr new_session(new chat_session(io_service_, room_)); // 1
acceptor_.async_accept(new_session->socket(), // 2
boost::bind(&chat_server::handle_accept, this, new_session, // 3
boost::asio::placeholders::error)); // 4
}
Line 1: A chat_session object is created which represents a session between one client and the server. A session is created for the accept (no client has connected yet).
Line 2: An asynchronous accept for the socket...
Line 3: ...bound to call chat_server::handle_accept when it happens. The session is passed along to be used by the first client which connects.
Now, if we look at the handle_accept we see that upon client connect, start is called for the session (this just starts stuff between the server and this client). Lastly a new accept is put outstanding in case other clients want to connect as well.
void handle_accept(chat_session_ptr session,
const boost::system::error_code& error)
{
if (!error)
{
session->start();
}
start_accept();
}
This is what you want to have as well. An outstanding accept for incoming connections. And if multiple clients can connect, there should always be one of these outstanding so the server can handle the accept.
How the server and the client(s) interact is all in the session and you could follow the same design and modify this to do what you want. You mention that the server needs to look at what is sent and do different things. Take a look at chat_session and the start function which was called by the server in handle_accept.
void start()
{
room_.join(shared_from_this());
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(
&chat_session::handle_read_header, shared_from_this(),
boost::asio::placeholders::error));
}
What is important here is the call to boost::asio::async_read. This is what you want too. This puts an outstanding read on the socket, so the server can read what the client sends. There is a handler (function) which is bound to this event chat_session::handle_read_header. This will be called whenever the server reads something on the socket. In this handler function you could start putting your specific code to determine what to do if a specific message is sent and so on.
What is important to know is that whenever calling these asynchronous boost::asio functions things will not happen within that call (i.e. the socket is not read if you call the function read). This is the asynchronous aspect. You just kind of register a handler for something and your code is called back when this happens. Hence, when this read is called it will immediately return and you're back in the handle_accept for the server (if you follow how things get called). And if you remember there we also call start_accept to set up another asynchronous accept. At this point you have two outstanding handlers waiting for either another client to connect or the first client to send something. Depending on what happens first, that specific handler will be called.
Also what is important to understand is that whenever something is run, it will run uninterrupted until everything it needs to do has been done. Other handlers have to wait even if there is are outstanding events which trigger them.
Finally, in order to run the server you'll need the io_service which is a central concept in Asio.
io_service.run();
This is one line you see in the main function. This just says that the thread (only one in the example) should run the io_service, which is the queue where handlers get enqueued when there is work to be done. When nothing, the io_service just waits (blocking the main thread there of course).
I hope this helps you get started with what you want to do. There is a lot of stuff you can do and things to learn. I find it a great piece of software! Good luck!
In case anyone else wants to do this, here is the minimum to get above going: (similar to the tutorials, but a bit shorter and a bit different)
class Session : public boost::enable_shared_from_this<Session>
{
tcp::socket socket;
char buf[1000];
public:
Session(boost::asio::io_service& io_service)
: socket(io_service) { }
tcp::socket& SocketRef() { return socket; }
void Read() {
boost::asio::async_read( socket,boost::asio::buffer(buf),boost::asio::transfer_at_least(1),boost::bind(&Session::Handle_Read,shared_from_this(),boost::asio::placeholders::error));
}
void Handle_Read(const boost::system::error_code& error) {
if (!error)
{
//read from buffer and handle requests
//if you want to write sth, you can do it sync. here: e.g. boost::asio::write(socket, ..., ignored_error);
Read();
}
}
};
typedef boost::shared_ptr<Session> SessionPtr;
class Server
{
boost::asio::io_service io_service;
tcp::acceptor acceptor;
public:
Server() : acceptor(io_service,tcp::endpoint(tcp::v4(), 13)) { }
~Server() { }
void operator()() { StartAccept(); io_service.run(); }
void StartAccept() {
SessionPtr session_ptr(new Session(io_service));
acceptor.async_accept(session_ptr->SocketRef(),boost::bind(&Server::HandleAccept,this,session_ptr,boost::asio::placeholders::error));
}
void HandleAccept(SessionPtr session,const boost::system::error_code& error) {
if (!error)
session->Read();
StartAccept();
}
};
From what I gathered through trial and error and reading: I kick it off in the operator()() so you can have it run in the background in an additional thread. You run one Server instance. To handle multiple clients, you need an extra class, I called this a session class. For asio to clean up dead sessions, you need a shared pointer as pointed out above. Otherwise the code should get you started.